Improvements to maneuver override processing (#6215)

This change unblocks the osrm-extract debug build, which is
currently failing on a maneuver override assertion.

The processing of maneuver overrides currently has three issues
- It assumes the via node(s) can't be compressed (the failing assertion)
- It can't handle via-paths containing incompressible nodes
- It doesn't interop with turn restriction on the same path

Turn restrictions and maneuver overrides both use the same
from-via-to path representation.
Therefore, we can fix these issues by consolidating their
structures and reusing the path representation for
turn restrictions, which already is robust to the above
issues.

This also simplifies some of the codebase by removing maneuver
override specific path processing.

There are ~100 maneuver overrides in the OSM database, so the
impact on processing and routing will be minimal.
This commit is contained in:
Michael Bell 2022-08-24 16:19:24 +01:00 committed by GitHub
parent 8e74b7af9d
commit a98074a051
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1224 additions and 1045 deletions

View File

@ -44,6 +44,7 @@
- CHANGED: Lazily generate optional route path data [#6045](https://github.com/Project-OSRM/osrm-backend/pull/6045)
- FIXED: Completed support for no_entry and no_exit turn restrictions. [#5988](https://github.com/Project-OSRM/osrm-backend/pull/5988)
- ADDED: Add support for non-round-trips with a single fixed endpoint. [#6050](https://github.com/Project-OSRM/osrm-backend/pull/6050)
- FIXED: Improvements to maneuver override processing [#6125](https://github.com/Project-OSRM/osrm-backend/pull/6125)
# 5.26.0
- Changes from 5.25.0

View File

@ -230,3 +230,100 @@ Feature: Maneuver tag support
| waypoints | route | turns |
| z,t | NY Ave,395,395 | depart,on ramp left,arrive |
| z,b | NY Ave,,4th St,4th St | depart,on ramp left,fork slight right,arrive |
Scenario: Gracefully handles maneuvers that are redundant for the profile
Given the node map
"""
a--b---c---d----f
|
|
e
"""
And the ways
| nodes | name | oneway | highway |
| abc | A Street | no | primary |
| ce | B Street | no | construction |
| cdf | A Street | no | primary |
And the relations
| type | way:from | node:via | way:to | maneuver | direction |
| maneuver | abc | c | cdf | turn | slight_left |
When I route I should get
| waypoints | route | turns |
| a,f | A Street,A Street | depart,arrive |
Scenario: Handles uncompressed nodes in maneuver path
Given the node map
"""
a--b---c---f
| |
| |
g d---h
|
|
i-------e-------j
"""
And the ways
| nodes | name | oneway |
| abc | A Street | no |
| cf | B Street | no |
| cde | C Street | no |
| bg | D Street | no |
| dh | E Street | no |
| ei | F Street | no |
| ej | G Street | no |
And the relations
| type | way:from | node:via | way:via | way:to | maneuver | direction |
| maneuver | abc | e | cde | ei | turn | sharp_right |
When I route I should get
| waypoints | route | turns |
| a,i | A Street,C Street,F Street,F Street | depart,turn right,turn sharp right,arrive |
Scenario: Can be used with turn restrictions
Given the node map
"""
a---b---c
|
|
d
|
e---f
|
|
h------g---i
"""
And the ways
| nodes | name | oneway |
| ab | A Street | no |
| bc | B Street | no |
| bde | C Street | no |
| ef | D Street | no |
| eg | E Street | no |
| hg | F Street | no |
| gi | G Street | no |
And the relations
| type | way:from | node:via | way:to | maneuver | direction | # |
| maneuver | ab | b | bde | turn | sharp_right | ending on a turn restriction via way |
| maneuver | bde | e | ef | turn | sharp_left | starting on a turn restriction via way |
And the relations
| type | way:from | node:via | way:via | way:to | maneuver | direction | # |
| maneuver | cb | g | bde,eg | gi | turn | slight_left | turn restricted |
| maneuver | cb | g | bde,eg | hg | turn | slight_right | not turn restricted |
And the relations
| type | way:from | way:via | way:to | restriction |
| restriction | ab | bde,eg | hg | no_right_turn |
| restriction | bc | bde,eg | gi | no_left_turn |
When I route I should get
| waypoints | route | turns |
| a,e | A Street,C Street,C Street | depart,turn sharp right,arrive |
| b,f | C Street,D Street,D Street | depart,turn sharp left,arrive |
| c,h | B Street,E Street,F Street,F Street | depart,turn left,turn slight right,arrive |
| c,i | B Street,A Street,E Street,G Street,G Street | depart,turn uturn,turn right,end of road left,arrive |

View File

@ -4,7 +4,6 @@
#include "extractor/scripting_environment.hpp"
#include "util/typedefs.hpp"
#include "extractor/maneuver_override.hpp"
#include "util/node_based_graph.hpp"
#include <memory>
@ -18,6 +17,7 @@ namespace extractor
class CompressedEdgeContainer;
struct TurnRestriction;
struct UnresolvedManeuverOverride;
class GraphCompressor
{

View File

@ -5,9 +5,13 @@
#include "util/typedefs.hpp"
#include "storage/shared_memory_ownership.hpp"
#include "turn_path.hpp"
#include "util/integer_range.hpp"
#include "util/log.hpp"
#include "util/vector_view.hpp"
#include <algorithm>
#include <boost/functional/hash.hpp>
#include <mapbox/variant.hpp>
namespace osrm
{
@ -17,7 +21,7 @@ namespace extractor
// Data that is loaded from the OSM datafile directly
struct InputManeuverOverride
{
std::vector<OSMWayID> via_ways;
InputTurnPath turn_path;
OSMNodeID via_node;
std::string maneuver;
std::string direction;
@ -26,9 +30,7 @@ struct InputManeuverOverride
// Object returned by the datafacade
struct ManeuverOverride
{
// util::ViewOrVector<NodeID, storage::Ownership::View> node_sequence;
std::vector<NodeID> node_sequence;
// before the turn, then later, the edge_based_node_id of the turn
NodeID instruction_node; // node-based node ID
guidance::TurnType::Enum override_type;
guidance::DirectionModifier::Enum direction;
@ -40,12 +42,12 @@ struct StorageManeuverOverride
std::uint32_t node_sequence_offset_begin;
std::uint32_t node_sequence_offset_end;
NodeID start_node;
// before the turn, then later, the edge_based_node_id of the turn
NodeID instruction_node; // node-based node ID
guidance::TurnType::Enum override_type;
guidance::DirectionModifier::Enum direction;
};
// Used to identify maneuver turns whilst generating edge-based graph
struct NodeBasedTurn
{
NodeID from;
@ -58,29 +60,88 @@ struct NodeBasedTurn
}
};
// Internal representation of maneuvers during graph extraction phase
struct UnresolvedManeuverOverride
{
std::vector<NodeBasedTurn>
turn_sequence; // initially the internal node-based-node ID of the node
// before the turn, then later, the edge_based_node_id of the turn
// The turn sequence that the maneuver override applies to.
TurnPath turn_path;
NodeID instruction_node; // node-based node ID
guidance::TurnType::Enum override_type;
guidance::DirectionModifier::Enum direction;
UnresolvedManeuverOverride()
{
turn_path = {ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}};
instruction_node = SPECIAL_NODEID;
override_type = guidance::TurnType::Invalid;
direction = guidance::DirectionModifier::MaxDirectionModifier;
}
// check if all parts of the restriction reference an actual node
bool Valid() const
{
return !turn_sequence.empty() &&
std::none_of(turn_sequence.begin(),
turn_sequence.end(),
[](const auto &n) {
return n.from == SPECIAL_NODEID || n.via == SPECIAL_NODEID ||
n.to == SPECIAL_NODEID;
}) &&
(direction != guidance::DirectionModifier::MaxDirectionModifier ||
override_type != guidance::TurnType::Invalid);
if ((direction == guidance::DirectionModifier::MaxDirectionModifier &&
override_type == guidance::TurnType::Invalid) ||
instruction_node == SPECIAL_NODEID || !turn_path.Valid())
{
return false;
}
if (turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH)
{
const auto node_path = turn_path.AsViaNodePath();
if (node_path.via != instruction_node)
{
util::Log(logDEBUG) << "Maneuver via-node " << node_path.via
<< " does not match instruction node " << instruction_node;
return false;
}
}
else
{
BOOST_ASSERT(turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH);
const auto way_path = turn_path.AsViaWayPath();
if (std::find(way_path.via.begin(), way_path.via.end(), instruction_node) ==
way_path.via.end())
{
util::Log(logDEBUG) << "Maneuver via-way path does not contain instruction node "
<< instruction_node;
return false;
}
}
return true;
}
// Generate sequence of node-based-node turns. Used to identify the maneuver's edge-based-node
// turns during graph expansion.
std::vector<NodeBasedTurn> Turns() const
{
if (turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH)
{
const auto node_maneuver = turn_path.AsViaNodePath();
return {{node_maneuver.from, node_maneuver.via, node_maneuver.to}};
}
BOOST_ASSERT(turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH);
std::vector<NodeBasedTurn> result;
const auto way_maneuver = turn_path.AsViaWayPath();
BOOST_ASSERT(way_maneuver.via.size() >= 2);
result.push_back({way_maneuver.from, way_maneuver.via[0], way_maneuver.via[1]});
for (auto i : util::irange<size_t>(0, way_maneuver.via.size() - 2))
{
result.push_back(
{way_maneuver.via[i], way_maneuver.via[i + 1], way_maneuver.via[i + 2]});
}
result.push_back({way_maneuver.via[way_maneuver.via.size() - 2],
way_maneuver.via.back(),
way_maneuver.to});
return result;
}
static std::string Name() { return "maneuver override"; };
};
} // namespace extractor
} // namespace osrm

View File

@ -6,6 +6,7 @@
#include "util/typedefs.hpp"
#include "mapbox/variant.hpp"
#include "turn_path.hpp"
#include <limits>
namespace osrm
@ -13,282 +14,54 @@ namespace osrm
namespace extractor
{
// OSM offers two types of restrictions, via node and via-way restrictions. We parse both into the
// same input container
//
// A restriction turning at a single node. This is the most common type of restriction:
//
// a - b - c
// |
// d
//
// ab via b to bd
struct InputNodeRestriction
{
OSMWayID from;
OSMNodeID via;
OSMWayID to;
};
// A restriction that uses one or more via-way in between
//
// e - f - g
// |
// d
// |
// a - b - c
//
// ab via bd,df to fe -- no u turn
struct InputWayRestriction
{
OSMWayID from;
std::vector<OSMWayID> via;
OSMWayID to;
};
// Outside view of the variant, these are equal to the `which()` results
enum RestrictionType
{
NODE_RESTRICTION = 0,
WAY_RESTRICTION = 1,
NUM_RESTRICTION_TYPES = 2
};
// External (OSM) representation of restriction
struct InputTurnRestriction
{
// keep in the same order as the turn restrictions below
mapbox::util::variant<InputNodeRestriction, InputWayRestriction> node_or_way;
InputTurnPath turn_path;
bool is_only;
// We represent conditional and unconditional restrictions with the same structure.
// Unconditional restrictions will have empty conditions.
std::vector<util::OpeningHours> condition;
OSMWayID From() const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? mapbox::util::get<InputNodeRestriction>(node_or_way).from
: mapbox::util::get<InputWayRestriction>(node_or_way).from;
}
OSMWayID To() const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? mapbox::util::get<InputNodeRestriction>(node_or_way).to
: mapbox::util::get<InputWayRestriction>(node_or_way).to;
}
RestrictionType Type() const
{
BOOST_ASSERT(node_or_way.which() < RestrictionType::NUM_RESTRICTION_TYPES);
return static_cast<RestrictionType>(node_or_way.which());
}
InputWayRestriction &AsWayRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return mapbox::util::get<InputWayRestriction>(node_or_way);
}
const InputWayRestriction &AsWayRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return mapbox::util::get<InputWayRestriction>(node_or_way);
}
InputNodeRestriction &AsNodeRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return mapbox::util::get<InputNodeRestriction>(node_or_way);
}
const InputNodeRestriction &AsNodeRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return mapbox::util::get<InputNodeRestriction>(node_or_way);
}
};
// OSRM manages restrictions based on node IDs which refer to the last node along the edge. Note
// that this has the side-effect of not allowing parallel edges!
//
// a - b - c
// |
// d
//
// ab via b to bd
struct NodeRestriction
{
NodeID from;
NodeID via;
NodeID to;
// check if all parts of the restriction reference an actual node
bool Valid() const
{
return from != SPECIAL_NODEID && to != SPECIAL_NODEID && via != SPECIAL_NODEID;
};
bool operator==(const NodeRestriction &other) const
{
return std::tie(from, via, to) == std::tie(other.from, other.via, other.to);
}
};
// A way restriction in the context of OSRM requires translation into NodeIDs. This is due to the
// compression happening in the graph creation process which would make it difficult to track
// way-ids over a series of operations. Having access to the nodes directly allows look-up of the
// edges in the processed structures
//
// e - f - g
// |
// d
// |
// a - b - c
//
// ab via bd,df to fe -- no u turn
struct WayRestriction
{
// A way restriction in OSRM needs to track all nodes that make up the via ways. Whilst most
// of these nodes will be removed by compression, some nodes will contain features that need to
// be considered when routing (e.g. intersections, nested restrictions, etc).
NodeID from;
std::vector<NodeID> via;
NodeID to;
// check if all parts of the restriction reference an actual node
bool Valid() const
{
return from != SPECIAL_NODEID && to != SPECIAL_NODEID && via.size() >= 2 &&
std::all_of(via.begin(), via.end(), [](NodeID i) { return i != SPECIAL_NODEID; });
};
bool operator==(const WayRestriction &other) const
{
return std::tie(from, via, to) == std::tie(other.from, other.via, other.to);
}
};
// Wrapper for turn restrictions that gives more information on its type / handles the switch
// between node/way restrictions
// Internal (OSRM) representation of restriction
struct TurnRestriction
{
// keep in the same order as the turn restrictions above
mapbox::util::variant<NodeRestriction, WayRestriction> node_or_way;
// The turn sequence that the restriction applies to.
TurnPath turn_path;
// Indicates if the restriction turn *must* or *must not* be taken.
bool is_only;
// We represent conditional and unconditional restrictions with the same structure.
// Unconditional restrictions will have empty conditions.
std::vector<util::OpeningHours> condition;
// construction for NodeRestrictions
explicit TurnRestriction(NodeRestriction node_restriction, bool is_only = false)
: node_or_way(node_restriction), is_only(is_only)
{
}
// construction for WayRestrictions
explicit TurnRestriction(const WayRestriction &way_restriction, bool is_only = false)
: node_or_way(way_restriction), is_only(is_only)
explicit TurnRestriction(const TurnPath &turn_path, bool is_only = false)
: turn_path(turn_path), is_only(is_only)
{
}
explicit TurnRestriction()
{
node_or_way = NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
turn_path = {ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}};
}
NodeID To() const
bool IsTurnRestricted(NodeID to) const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? mapbox::util::get<NodeRestriction>(node_or_way).to
: mapbox::util::get<WayRestriction>(node_or_way).to;
return is_only ? turn_path.To() != to : turn_path.To() == to;
}
NodeID From() const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? mapbox::util::get<NodeRestriction>(node_or_way).from
: mapbox::util::get<WayRestriction>(node_or_way).from;
}
NodeID FirstVia() const
{
if (node_or_way.which() == RestrictionType::NODE_RESTRICTION)
{
return mapbox::util::get<NodeRestriction>(node_or_way).via;
}
else
{
BOOST_ASSERT(!mapbox::util::get<WayRestriction>(node_or_way).via.empty());
return mapbox::util::get<WayRestriction>(node_or_way).via[0];
}
}
bool IsTurnRestricted(NodeID to) const { return is_only ? To() != to : To() == to; }
bool IsUnconditional() const { return condition.empty(); }
WayRestriction &AsWayRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return mapbox::util::get<WayRestriction>(node_or_way);
}
const WayRestriction &AsWayRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return mapbox::util::get<WayRestriction>(node_or_way);
}
NodeRestriction &AsNodeRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return mapbox::util::get<NodeRestriction>(node_or_way);
}
const NodeRestriction &AsNodeRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return mapbox::util::get<NodeRestriction>(node_or_way);
}
RestrictionType Type() const
{
BOOST_ASSERT(node_or_way.which() < RestrictionType::NUM_RESTRICTION_TYPES);
return static_cast<RestrictionType>(node_or_way.which());
}
// check if all elements of the edge are considered valid
bool Valid() const
{
if (node_or_way.which() == RestrictionType::WAY_RESTRICTION)
{
auto const &restriction = AsWayRestriction();
return restriction.Valid();
}
else
{
auto const &restriction = AsNodeRestriction();
return restriction.Valid();
}
}
bool Valid() const { return turn_path.Valid(); }
bool operator==(const TurnRestriction &other) const
{
if (is_only != other.is_only)
return false;
if (Type() != other.Type())
return false;
if (Type() == RestrictionType::WAY_RESTRICTION)
{
return AsWayRestriction() == other.AsWayRestriction();
}
else
{
return AsNodeRestriction() == other.AsNodeRestriction();
}
return turn_path == other.turn_path;
}
static std::string Name() { return "turn restriction"; };
};
} // namespace extractor
} // namespace osrm

View File

@ -1,56 +0,0 @@
#ifndef OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_
#define OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_
#include "extractor/maneuver_override.hpp"
#include "extractor/restriction.hpp"
#include "util/typedefs.hpp"
#include <boost/unordered_map.hpp>
#include <vector>
namespace osrm
{
namespace extractor
{
struct NodeRestriction;
struct TurnRestriction;
// OSRM stores restrictions as node -> [node] -> node instead of way -> node -> way (or
// way->[way]->way) as it is done in OSM. These restrictions need to match the state of graph
// compression which we perform in the graph compressor that removes certain degree two nodes from
// the graph (all but the ones with penalties/barriers, as of the state of writing).
// Since this graph compression ins performed after creating the restrictions in the extraction
// phase, we need to update the involved nodes whenever one of the nodes is compressed.
//
//
// !!!! Will bind to the restrictions vector and modify it in-place !!!!
class RestrictionCompressor
{
public:
RestrictionCompressor(std::vector<TurnRestriction> &restrictions,
std::vector<UnresolvedManeuverOverride> &maneuver_overrides);
// account for the compression of `from-via-to` into `from-to`
void Compress(const NodeID from, const NodeID via, const NodeID to);
private:
// A turn restriction is given as `from star via node to end`. Edges ending at `head` being
// contracted move the head pointer to their respective head. Edges starting at tail move the
// tail values to their respective tails.
// Via nodes that are compressed are removed from the restriction representation.
// We do not compress the first and last via nodes of a restriction as they act as
// entrance/exit points into the restriction graph. For a node restriction, the first and last
// via nodes are the same.
boost::unordered_multimap<NodeID, TurnRestriction *> starts;
boost::unordered_multimap<NodeID, TurnRestriction *> vias;
boost::unordered_multimap<NodeID, TurnRestriction *> ends;
boost::unordered_multimap<NodeID, NodeBasedTurn *> maneuver_starts;
boost::unordered_multimap<NodeID, NodeBasedTurn *> maneuver_ends;
};
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_

View File

@ -1,22 +0,0 @@
#ifndef OSRM_EXTRACTOR_RESTRICTION_FILTER_HPP_
#define OSRM_EXTRACTOR_RESTRICTION_FILTER_HPP_
#include "extractor/restriction.hpp"
#include "util/node_based_graph.hpp"
#include <vector>
namespace osrm
{
namespace extractor
{
// To avoid handling invalid restrictions / creating unnecessary duplicate nodes for via-ways, we do
// a pre-flight check for restrictions and remove all invalid restrictions from the data. Use as
// `restrictions = removeInvalidRestrictions(std::move(restrictions))`
std::vector<TurnRestriction> removeInvalidRestrictions(std::vector<TurnRestriction>,
const util::NodeBasedDynamicGraph &);
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_RESTRICTION_FILTER_HPP_

View File

@ -4,7 +4,6 @@
#include <boost/assert.hpp>
#include <boost/unordered_map.hpp>
#include "extractor/restriction_filter.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
@ -13,6 +12,8 @@ namespace osrm
namespace extractor
{
struct TurnRestriction;
namespace restriction_graph_details
{
struct transferBuilder;

View File

@ -0,0 +1,269 @@
#ifndef OSRM_TURN_PATH_HPP
#define OSRM_TURN_PATH_HPP
#include "util/typedefs.hpp"
#include <algorithm>
#include <mapbox/variant.hpp>
#include <vector>
namespace osrm
{
namespace extractor
{
// Outside view of the variant, these are equal to the `which()` results
enum TurnPathType
{
VIA_NODE_TURN_PATH = 0,
VIA_WAY_TURN_PATH = 1,
NUM_TURN_PATH_TYPES = 2
};
// OSM turn restrictions and maneuver overrides are relations that use the same path
// representation. Therefore, we can represent these paths by a shared, common structure.
//
// from: the way from which the turn sequence begins
// via: a node or list of ways, representing the intermediate path taken in the turn sequence
// to: the final way in the turn sequence
//
// We will have two representations of the paths, via-node and via-way, to represent the two options
// for the intermediate path. We parse both into the same input container
//
// A path turning at a single node. This is the most common type of relation:
//
// a - b - c
// |
// d
//
// ab via b to bd
struct InputViaNodePath
{
OSMWayID from;
OSMNodeID via;
OSMWayID to;
};
// A turn path that uses one or more via-way in between
//
// e - f - g
// |
// d
// |
// a - b - c
//
// ab via bd,df to fe
struct InputViaWayPath
{
OSMWayID from;
std::vector<OSMWayID> via;
OSMWayID to;
};
struct InputTurnPath
{
mapbox::util::variant<InputViaNodePath, InputViaWayPath> node_or_way;
TurnPathType Type() const
{
BOOST_ASSERT(node_or_way.which() < TurnPathType::NUM_TURN_PATH_TYPES);
return static_cast<TurnPathType>(node_or_way.which());
}
OSMWayID From() const
{
return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH
? mapbox::util::get<InputViaNodePath>(node_or_way).from
: mapbox::util::get<InputViaWayPath>(node_or_way).from;
}
OSMWayID To() const
{
return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH
? mapbox::util::get<InputViaNodePath>(node_or_way).to
: mapbox::util::get<InputViaWayPath>(node_or_way).to;
}
InputViaWayPath &AsViaWayPath()
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH);
return mapbox::util::get<InputViaWayPath>(node_or_way);
}
const InputViaWayPath &AsViaWayPath() const
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH);
return mapbox::util::get<InputViaWayPath>(node_or_way);
}
InputViaNodePath &AsViaNodePath()
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH);
return mapbox::util::get<InputViaNodePath>(node_or_way);
}
const InputViaNodePath &AsViaNodePath() const
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH);
return mapbox::util::get<InputViaNodePath>(node_or_way);
}
};
// Internally, we convert the turn paths into a node-based-node representation.
// This allows us to correctly track the edges as they processed, such as during graph compression.
// Having access to the nodes directly allows look-up of the edges in the processed structures,
// and can be utilised during edge-based-graph generation.
//
// Once again, we keep two representations of the paths, via-node and via-way, for more efficient
// representation of the more common via-node path.
//
// a - b - c
// |
// d
//
// a via b to d
struct ViaNodePath
{
NodeID from;
NodeID via;
NodeID to;
// check if all parts of the restriction reference an actual node
bool Valid() const
{
return from != SPECIAL_NODEID && to != SPECIAL_NODEID && via != SPECIAL_NODEID;
};
bool operator==(const ViaNodePath &other) const
{
return std::tie(from, via, to) == std::tie(other.from, other.via, other.to);
}
};
//
//
// e - f - g
// |
// d
// |
// a - b - c
//
// a via bdf to e
// (after compression) a via bf to e
struct ViaWayPath
{
// A way path in OSRM needs to track all nodes that make up the via ways. Whilst most
// of these nodes will be removed by compression, some nodes will contain features that need to
// be considered when routing (e.g. intersections, nested restrictions, etc).
NodeID from;
std::vector<NodeID> via;
NodeID to;
// check if all parts of the path reference an actual node
bool Valid() const
{
return from != SPECIAL_NODEID && to != SPECIAL_NODEID && via.size() >= 2 &&
std::all_of(via.begin(), via.end(), [](NodeID i) { return i != SPECIAL_NODEID; });
};
bool operator==(const ViaWayPath &other) const
{
return std::tie(from, via, to) == std::tie(other.from, other.via, other.to);
}
};
// Wrapper for turn paths that gives more information on its type / handles the switch
// between node/way paths
struct TurnPath
{
mapbox::util::variant<ViaNodePath, ViaWayPath> node_or_way;
NodeID To() const
{
return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH
? mapbox::util::get<ViaNodePath>(node_or_way).to
: mapbox::util::get<ViaWayPath>(node_or_way).to;
}
NodeID From() const
{
return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH
? mapbox::util::get<ViaNodePath>(node_or_way).from
: mapbox::util::get<ViaWayPath>(node_or_way).from;
}
NodeID FirstVia() const
{
if (node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH)
{
return mapbox::util::get<ViaNodePath>(node_or_way).via;
}
else
{
BOOST_ASSERT(!mapbox::util::get<ViaWayPath>(node_or_way).via.empty());
return mapbox::util::get<ViaWayPath>(node_or_way).via[0];
}
}
ViaWayPath &AsViaWayPath()
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH);
return mapbox::util::get<ViaWayPath>(node_or_way);
}
const ViaWayPath &AsViaWayPath() const
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH);
return mapbox::util::get<ViaWayPath>(node_or_way);
}
ViaNodePath &AsViaNodePath()
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH);
return mapbox::util::get<ViaNodePath>(node_or_way);
}
const ViaNodePath &AsViaNodePath() const
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH);
return mapbox::util::get<ViaNodePath>(node_or_way);
}
TurnPathType Type() const
{
BOOST_ASSERT(node_or_way.which() < TurnPathType::NUM_TURN_PATH_TYPES);
return static_cast<TurnPathType>(node_or_way.which());
}
bool operator==(const TurnPath &other) const
{
if (Type() != other.Type())
return false;
if (Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
return AsViaWayPath() == other.AsViaWayPath();
}
else
{
return AsViaNodePath() == other.AsViaNodePath();
}
}
bool Valid() const
{
if (Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
return AsViaWayPath().Valid();
}
else
{
return AsViaNodePath().Valid();
}
};
};
} // namespace extractor
} // namespace osrm
#endif // OSRM_TURN_PATH_HPP

View File

@ -0,0 +1,54 @@
#ifndef OSRM_EXTRACTOR_TURN_PATH_COMPRESSOR_HPP_
#define OSRM_EXTRACTOR_TURN_PATH_COMPRESSOR_HPP_
#include "util/typedefs.hpp"
#include <boost/unordered_map.hpp>
#include <vector>
namespace osrm
{
namespace extractor
{
struct TurnPath;
struct TurnRestriction;
struct UnresolvedManeuverOverride;
// OSRM stores turn paths as node -> [node] -> node instead of way -> node -> way (or
// way->[way]->way) as it is done in OSM. These paths need to match the state of graph
// compression which we perform in the graph compressor that removes certain degree two nodes from
// the graph (all but the ones with penalties/barriers, as of the state of writing).
// Since this graph compression is performed after creating the turn paths in the extraction
// phase, we need to update the involved nodes whenever one of the nodes is compressed.
//
//
// !!!! Will bind to the restriction/maneuver vectors and modify it in-place !!!!
class TurnPathCompressor
{
public:
TurnPathCompressor(std::vector<TurnRestriction> &restrictions,
std::vector<UnresolvedManeuverOverride> &maneuver_overrides);
// account for the compression of `from-via-to` into `from-to`
void Compress(const NodeID from, const NodeID via, const NodeID to);
private:
// A turn path is given as `from start via node(s) to end`. Edges ending at `head` being
// contracted move the head pointer to their respective head. Edges starting at tail move the
// tail values to their respective tails.
// Via nodes that are compressed are removed from the restriction representation.
// We do not compress the first and last via nodes of a restriction as they act as
// entrance/exit points into the restriction graph. For a node restriction, the first and last
// via nodes are the same.
// Similarly, we do not compress the instruction via node in a maneuver override, as we need
// this to identify the location of the maneuver during routing path-processing.
boost::unordered_multimap<NodeID, TurnPath *> starts;
boost::unordered_multimap<NodeID, TurnPath *> vias;
boost::unordered_multimap<NodeID, TurnPath *> ends;
};
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_TURN_PATH_COMPRESSOR_HPP_

View File

@ -0,0 +1,22 @@
#ifndef OSRM_EXTRACTOR_TURN_PATH_FILTER_HPP_
#define OSRM_EXTRACTOR_TURN_PATH_FILTER_HPP_
#include "extractor/restriction.hpp"
#include "util/node_based_graph.hpp"
#include <vector>
namespace osrm
{
namespace extractor
{
// To avoid handling invalid turn paths / creating unnecessary duplicate nodes for via-ways, we do
// a pre-flight check for paths and remove all invalid turn relations from the data. Use as
// `restrictions = removeInvalidRestrictions(std::move(restrictions))`
template <typename T>
std::vector<T> removeInvalidTurnPaths(std::vector<T>, const util::NodeBasedDynamicGraph &);
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_TURN_PATH_FILTER_HPP_

View File

@ -10,24 +10,19 @@
#include "storage/io.hpp"
#include "util/assert.hpp"
#include "util/bearing.hpp"
#include "util/connectivity_checksum.hpp"
#include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/exception.hpp"
#include "util/integer_range.hpp"
#include "util/log.hpp"
#include "util/percent.hpp"
#include "util/timing_util.hpp"
#include <boost/assert.hpp>
#include <boost/crc.hpp>
#include <boost/functional/hash.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <limits>
#include <sstream>
#include <string>
@ -451,6 +446,17 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
{
util::Log() << "Generating edge-expanded edges ";
// Keep a set of all maneuver turns so we can identify them as
// we generate the edge-expansion.
std::unordered_set<NodeBasedTurn> unresolved_turns;
for (const auto &manuevers : unresolved_maneuver_overrides)
{
for (const auto &turn : manuevers.Turns())
{
unresolved_turns.insert(turn);
}
}
std::size_t node_based_edge_counter = 0;
SuffixTable street_name_suffix_table(scripting_environment);
@ -514,7 +520,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
std::vector<EdgeWithData> delayed_data; // may need this
std::vector<Conditional> conditionals;
std::unordered_map<NodeBasedTurn, std::pair<NodeID, NodeID>> turn_to_ebn_map;
std::unordered_multimap<NodeBasedTurn, std::pair<NodeID, NodeID>> turn_to_ebn_map;
util::ConnectivityChecksum checksum;
};
@ -522,7 +528,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
m_connectivity_checksum = 0;
std::unordered_map<NodeBasedTurn, std::pair<NodeID, NodeID>> global_turn_to_ebn_map;
std::unordered_multimap<NodeBasedTurn, std::pair<NodeID, NodeID>> global_turn_to_ebn_map;
// going over all nodes (which form the center of an intersection), we compute all possible
// turns along these intersections.
@ -894,24 +900,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
const auto outgoing_edge_target =
m_node_based_graph.GetTarget(outgoing_edge.edge);
// TODO: this loop is not optimized - once we have a few
// overrides available, we should index this for faster
// lookups
for (auto &override : unresolved_maneuver_overrides)
const auto turn_nodes = NodeBasedTurn{
incoming_edge.node, intersection_node, outgoing_edge_target};
const auto is_maneuver_turn = unresolved_turns.count(turn_nodes) > 0;
if (is_maneuver_turn)
{
for (auto &turn : override.turn_sequence)
{
if (turn.from == incoming_edge.node &&
turn.via == intersection_node &&
turn.to == outgoing_edge_target)
{
const auto &ebn_from =
nbe_to_ebn_mapping[incoming_edge.edge];
const auto &ebn_to = target_id;
buffer->turn_to_ebn_map[turn] =
std::make_pair(ebn_from, ebn_to);
}
}
const auto &ebn_from = nbe_to_ebn_mapping[incoming_edge.edge];
const auto &ebn_to = target_id;
buffer->turn_to_ebn_map.insert(
{turn_nodes, std::make_pair(ebn_from, ebn_to)});
}
{ // scope to forget edge_with_data after
@ -1025,6 +1023,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
m_coordinates[intersection_node],
restriction->condition}});
}
// We also need to track maneuvers that traverse duplicate
// edges
if (is_maneuver_turn)
{
const auto &ebn_from = from_id;
const auto &ebn_to = via_target_id;
buffer->turn_to_ebn_map.insert(
{turn_nodes, std::make_pair(ebn_from, ebn_to)});
}
}
else
{
@ -1053,6 +1061,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
edge_geometries);
buffer->delayed_data.push_back(edge_with_data);
// We also need to track maneuvers that traverse duplicate
// edges
if (is_maneuver_turn)
{
const auto &ebn_from = from_id;
const auto &ebn_to = via_target_id;
buffer->turn_to_ebn_map.insert(
{turn_nodes, std::make_pair(ebn_from, ebn_to)});
}
}
}
}
@ -1107,35 +1125,64 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
// Now, replace node-based-node ID values in the `node_sequence` with
// the edge-based-node values we found and stored in the `turn_to_ebn_map`
for (auto &unresolved_override : unresolved_maneuver_overrides)
for (const auto &unresolved_override : unresolved_maneuver_overrides)
{
StorageManeuverOverride storage_override;
storage_override.instruction_node = unresolved_override.instruction_node;
storage_override.override_type = unresolved_override.override_type;
storage_override.direction = unresolved_override.direction;
// There can be multiple edge-based-node sequences for a node-based-turn sequence
// due to duplicate edges in the restriction graph.
std::vector<std::vector<NodeID>> node_sequences;
std::vector<NodeID> node_sequence(unresolved_override.turn_sequence.size() + 1,
SPECIAL_NODEID);
const auto &turns = unresolved_override.Turns();
for (std::int64_t i = unresolved_override.turn_sequence.size() - 1; i >= 0; --i)
{
const auto v = global_turn_to_ebn_map.find(unresolved_override.turn_sequence[i]);
if (v != global_turn_to_ebn_map.end())
BOOST_ASSERT(!turns.empty());
// Populate the node sequences with the first turn values.
const auto first_turn_edges = global_turn_to_ebn_map.equal_range(turns[0]);
std::transform(
first_turn_edges.first,
first_turn_edges.second,
std::back_inserter(node_sequences),
[](const auto turn_edges) {
return std::vector<NodeID>{turn_edges.second.first, turn_edges.second.second};
});
std::for_each(std::next(turns.begin()), turns.end(), [&](const auto &turn) {
std::vector<std::vector<NodeID>> next_node_sequences;
const auto next_turn_edges = global_turn_to_ebn_map.equal_range(turn);
for (auto &node_sequence : node_sequences)
{
node_sequence[i] = v->second.first;
node_sequence[i + 1] = v->second.second;
const auto found_it = std::find_if(
next_turn_edges.first, next_turn_edges.second, [&](const auto &turn_edges) {
const auto pre_turn_edge = turn_edges.second.first;
return (node_sequence.back() == pre_turn_edge);
});
if (found_it != next_turn_edges.second)
{
const auto post_turn_edge = found_it->second.second;
node_sequence.push_back(post_turn_edge);
next_node_sequences.push_back(std::move(node_sequence));
}
}
node_sequences = std::move(next_node_sequences);
});
for (const auto &node_sequence : node_sequences)
{
StorageManeuverOverride storage_override;
storage_override.instruction_node = unresolved_override.instruction_node;
storage_override.override_type = unresolved_override.override_type;
storage_override.direction = unresolved_override.direction;
storage_override.node_sequence_offset_begin = maneuver_override_sequences.size();
storage_override.node_sequence_offset_end =
maneuver_override_sequences.size() + node_sequence.size();
storage_override.start_node = node_sequence.front();
maneuver_override_sequences.insert(
maneuver_override_sequences.end(), node_sequence.begin(), node_sequence.end());
storage_maneuver_overrides.push_back(storage_override);
}
storage_override.node_sequence_offset_begin = maneuver_override_sequences.size();
storage_override.node_sequence_offset_end =
maneuver_override_sequences.size() + node_sequence.size();
storage_override.start_node = node_sequence.front();
maneuver_override_sequences.insert(
maneuver_override_sequences.end(), node_sequence.begin(), node_sequence.end());
storage_maneuver_overrides.push_back(storage_override);
}
}
{

View File

@ -152,6 +152,228 @@ std::tuple<OSMNodeID, OSMNodeID, OSMNodeID> find_turn_nodes(const oe::NodesOfWay
}
return std::make_tuple(SPECIAL_OSM_NODEID, SPECIAL_OSM_NODEID, SPECIAL_OSM_NODEID);
}
// Via-node paths describe a relation between the two segments closest
// to the shared via-node on the from and to ways.
// from: [a, b, c, d, e]
// to: [f, g, h, i, j]
//
// The via node establishes the orientation of the from/to intersection when choosing the
// segments.
// via | node path
// a=f | b,a,g
// a=j | b,a,i
// e=f | d,e,g
// e=j | d,e,i
oe::ViaNodePath find_via_node_path(const std::string &turn_relation_type,
const oe::NodesOfWay &from_segment,
const oe::NodesOfWay &to_segment,
const OSMNodeID via_node,
const std::function<NodeID(OSMNodeID)> &to_internal_node)
{
OSMNodeID from, via, to;
std::tie(from, via, to) = find_turn_nodes(from_segment, to_segment, via_node);
if (via == SPECIAL_OSM_NODEID)
{
// unconnected
osrm::util::Log(logDEBUG) << turn_relation_type
<< " references unconnected way: " << from_segment.way_id;
return oe::ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
return oe::ViaNodePath{to_internal_node(from), to_internal_node(via), to_internal_node(to)};
}
// Via way paths are comprised of:
// 1. The segment in the from way that intersects with the via ways
// 2. All segments that make up the via path
// 3. The segment in the to way that intersects with the via path.
//
// from: [a, b, c, d, e]
// via: [[f, g, h, i, j], [k, l], [m, n, o]]
// to: [p, q, r, s]
//
// First establish the orientation of the from/via intersection by finding which end
// nodes both ways share. From this we can select the from segment.
//
// intersect | from segment | next_connection
// a=f | b,a | f
// a=j | b,a | j
// e=f | e,d | f
// e=j | e,d | j
//
// Use the next connection to inform the orientation of the first via
// way and the intersection between first and second via ways.
//
// next_connection | intersect | via result | next_next_connection
// f | j=k | [f,g,h,i,j] | k
// f | j=l | [f,g,h,i,j] | l
// j | f=k | [j,i,h,g,f] | k
// j | f=l | [j,i,h,g,f] | l
//
// This is continued for the remaining via ways, appending to the via result
//
// The final via/to intersection also uses the next_connection information in a similar fashion.
//
// next_connection | intersect | to_segment
// m | o=p | p,q
// m | o=s | s,r
// o | m=p | p,q
// o | m=s | s,r
//
// The final result is a list of nodes that represent a valid from->via->to path through the
// ways.
//
// E.g. if intersection nodes are a=j, f=l, k=o, m=s
// the result will be {e [d,c,b,a,i,h,g,f,k,n,m] r}
oe::ViaWayPath find_via_way_path(const std::string &turn_relation_type,
const oe::NodesOfWay &from_way,
const std::vector<oe::NodesOfWay> &via_ways,
const oe::NodesOfWay &to_way,
const std::function<NodeID(OSMNodeID)> &to_internal_node)
{
BOOST_ASSERT(!via_ways.empty());
oe::ViaWayPath way_path;
// Find the orientation of the connected ways starting with the from-via intersection.
OSMNodeID from, via;
std::tie(from, via, std::ignore) =
find_turn_nodes(from_way, via_ways.front(), SPECIAL_OSM_NODEID);
if (via == SPECIAL_OSM_NODEID)
{
osrm::util::Log(logDEBUG) << turn_relation_type
<< " has unconnected from and via ways: " << from_way.way_id
<< ", " << via_ways.front().way_id;
return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
way_path.from = to_internal_node(from);
way_path.via.push_back(to_internal_node(via));
// Use the connection node from the previous intersection to inform our conversion of
// via ways into internal nodes.
OSMNodeID next_connection = via;
for (const auto &via_way : via_ways)
{
if (next_connection == via_way.first_segment_source_id())
{
std::transform(std::next(via_way.node_ids.begin()),
via_way.node_ids.end(),
std::back_inserter(way_path.via),
to_internal_node);
next_connection = via_way.last_segment_target_id();
}
else if (next_connection == via_way.last_segment_target_id())
{
std::transform(std::next(via_way.node_ids.rbegin()),
via_way.node_ids.rend(),
std::back_inserter(way_path.via),
to_internal_node);
next_connection = via_way.first_segment_source_id();
}
else
{
osrm::util::Log(logDEBUG)
<< turn_relation_type << " has unconnected via way: " << via_way.way_id
<< " to node " << next_connection;
return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
}
// Add the final to node after the via-to intersection.
if (next_connection == to_way.first_segment_source_id())
{
way_path.to = to_internal_node(to_way.first_segment_target_id());
}
else if (next_connection == to_way.last_segment_target_id())
{
way_path.to = to_internal_node(to_way.last_segment_source_id());
}
else
{
osrm::util::Log(logDEBUG) << turn_relation_type
<< " has unconnected via and to ways: " << via_ways.back().way_id
<< ", " << to_way.way_id;
return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
return way_path;
}
// Check if we were able to resolve all the involved OSM elements before translating to an
// internal via-way turn path
oe::ViaWayPath
get_via_way_path_from_OSM_ids(const std::string &turn_relation_type,
const std::unordered_map<OSMWayID, oe::NodesOfWay> &referenced_ways,
const OSMWayID from_id,
const OSMWayID to_id,
const std::vector<OSMWayID> &via_ids,
const std::function<NodeID(OSMNodeID)> &to_internal_node)
{
auto const from_way_itr = referenced_ways.find(from_id);
if (from_way_itr->second.way_id != from_id)
{
osrm::util::Log(logDEBUG) << turn_relation_type
<< " references invalid from way: " << from_id;
return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
std::vector<oe::NodesOfWay> via_ways;
for (const auto &via_id : via_ids)
{
auto const via_segment_itr = referenced_ways.find(via_id);
if (via_segment_itr->second.way_id != via_id)
{
osrm::util::Log(logDEBUG)
<< turn_relation_type << " references invalid via way: " << via_id;
return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
via_ways.push_back(via_segment_itr->second);
}
auto const to_way_itr = referenced_ways.find(to_id);
if (to_way_itr->second.way_id != to_id)
{
osrm::util::Log(logDEBUG) << turn_relation_type << " references invalid to way: " << to_id;
return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
return find_via_way_path(
turn_relation_type, from_way_itr->second, via_ways, to_way_itr->second, to_internal_node);
}
// Check if we were able to resolve all the involved OSM elements before translating to an
// internal via-node turn path
oe::ViaNodePath
get_via_node_path_from_OSM_ids(const std::string &turn_relation_type,
const std::unordered_map<OSMWayID, oe::NodesOfWay> &referenced_ways,
const OSMWayID from_id,
const OSMWayID to_id,
const OSMNodeID via_node,
const std::function<NodeID(OSMNodeID)> &to_internal_node)
{
auto const from_segment_itr = referenced_ways.find(from_id);
if (from_segment_itr->second.way_id != from_id)
{
osrm::util::Log(logDEBUG) << turn_relation_type << " references invalid way: " << from_id;
return oe::ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
auto const to_segment_itr = referenced_ways.find(to_id);
if (to_segment_itr->second.way_id != to_id)
{
osrm::util::Log(logDEBUG) << turn_relation_type << " references invalid way: " << to_id;
return oe::ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
return find_via_node_path(turn_relation_type,
from_segment_itr->second,
to_segment_itr->second,
via_node,
to_internal_node);
}
} // namespace
namespace osrm
@ -177,10 +399,10 @@ ExtractionContainers::ExtractionContainers()
* Processes the collected data and serializes it.
* At this point nodes are still referenced by their OSM id.
*
* - map start-end nodes of ways to ways used in restrictions to compute compressed
* trippe representation
* - filter nodes list to nodes that are referenced by ways
* - merge edges with nodes to include location of start/end points and serialize
* - Identify nodes of ways used in restrictions and maneuver overrides
* - Filter nodes list to nodes that are referenced by ways
* - Prepare edges and compute routing properties
* - Prepare and validate restrictions and maneuver overrides
*
*/
void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environment,
@ -723,11 +945,17 @@ ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyManeuverOverr
const auto mark_ids = [&](auto const &external_maneuver_override) {
NodesOfWay dummy_segment{MAX_OSM_WAYID, {MAX_OSM_NODEID, MAX_OSM_NODEID}};
std::for_each(external_maneuver_override.via_ways.begin(),
external_maneuver_override.via_ways.end(),
[&maneuver_override_ways, dummy_segment](const auto &element) {
maneuver_override_ways[element] = dummy_segment;
});
const auto &turn_path = external_maneuver_override.turn_path;
maneuver_override_ways[turn_path.From()] = dummy_segment;
maneuver_override_ways[turn_path.To()] = dummy_segment;
if (external_maneuver_override.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
const auto &way = turn_path.AsViaWayPath();
for (const auto &via : way.via)
{
maneuver_override_ways[via] = dummy_segment;
}
}
};
// First, make an empty hashtable keyed by the ways referenced
@ -767,36 +995,6 @@ void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuv
return internal;
};
auto const get_turn_from_way_pair = [&](const OSMWayID &from_id, const OSMWayID &to_id) {
auto const from_segment_itr = maneuver_override_ways.find(from_id);
if (from_segment_itr->second.way_id != from_id)
{
util::Log(logDEBUG) << "Override references invalid way: " << from_id;
return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
auto const to_segment_itr = maneuver_override_ways.find(to_id);
if (to_segment_itr->second.way_id != to_id)
{
util::Log(logDEBUG) << "Override references invalid way: " << to_id;
return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
OSMNodeID from, via, to;
std::tie(from, via, to) =
find_turn_nodes(from_segment_itr->second, to_segment_itr->second, SPECIAL_OSM_NODEID);
if (via == SPECIAL_OSM_NODEID)
{
// unconnected
util::Log(logDEBUG) << "Maneuver override ways " << from_segment_itr->second.way_id
<< " and " << to_segment_itr->second.way_id << " are not connected";
return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
return NodeBasedTurn{osm_node_to_internal_nbn(from),
osm_node_to_internal_nbn(via),
osm_node_to_internal_nbn(to)};
};
const auto strings_to_turn_type_and_direction = [](const std::string &turn_string,
const std::string &direction_string) {
auto result = std::make_pair(guidance::TurnType::MaxTurnType,
@ -862,44 +1060,36 @@ void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuv
// Returns true on successful transformation, false in case of invalid references.
// Later, the UnresolvedManeuverOverride will be converted into a final ManeuverOverride
// once the edge-based-node IDs are generated by the edge-based-graph-factory
const auto transform = [&](const auto &external, auto &internal) {
// Create a stub override
auto maneuver_override =
UnresolvedManeuverOverride{{},
osm_node_to_internal_nbn(external.via_node),
guidance::TurnType::Invalid,
guidance::DirectionModifier::MaxDirectionModifier};
// Convert Way IDs into node-based-node IDs
// We iterate from back to front here because the first node in the node_sequence
// must eventually be a source node, but all the others must be targets.
// the get_internal_pairs_from_ways returns (source,target), so if we
// iterate backwards, we will end up with source,target,target,target,target
// in a sequence, which is what we want
for (auto i = 0ul; i < external.via_ways.size() - 1; ++i)
const auto transform = [&](const auto &external_type, auto &internal_type) {
if (external_type.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
// returns the two far ends of the referenced ways
auto turn = get_turn_from_way_pair(external.via_ways[i], external.via_ways[i + 1]);
maneuver_override.turn_sequence.push_back(turn);
auto const &external = external_type.turn_path.AsViaWayPath();
internal_type.turn_path.node_or_way =
get_via_way_path_from_OSM_ids(internal_type.Name(),
maneuver_override_ways,
external.from,
external.to,
external.via,
osm_node_to_internal_nbn);
}
else
{
BOOST_ASSERT(external_type.turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH);
auto const &external = external_type.turn_path.AsViaNodePath();
internal_type.turn_path.node_or_way =
get_via_node_path_from_OSM_ids(internal_type.Name(),
maneuver_override_ways,
external.from,
external.to,
external.via,
osm_node_to_internal_nbn);
}
// check if we were able to resolve all the involved ways
// auto maneuver_override =
// get_maneuver_override_from_OSM_ids(external.from, external.to,
// external.via_node);
internal_type.instruction_node = osm_node_to_internal_nbn(external_type.via_node),
std::tie(internal_type.override_type, internal_type.direction) =
strings_to_turn_type_and_direction(external_type.maneuver, external_type.direction);
std::tie(maneuver_override.override_type, maneuver_override.direction) =
strings_to_turn_type_and_direction(external.maneuver, external.direction);
if (!maneuver_override.Valid())
{
util::Log(logDEBUG) << "Override is invalid";
return false;
}
internal = std::move(maneuver_override);
return true;
return internal_type.Valid();
};
const auto transform_into_internal_types =
@ -925,7 +1115,7 @@ void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuv
ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyRestrictionWays()
{
// Contains the nodes of each way that is part of an restriction
// Contains the nodes of each way that is part of a restriction
ReferencedWays restriction_ways;
// Prepare for extracting nodes for all restrictions
@ -937,23 +1127,17 @@ ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyRestrictionWa
// nodes of these ways.
const auto mark_ids = [&](auto const &turn_restriction) {
NodesOfWay dummy_segment{MAX_OSM_WAYID, {MAX_OSM_NODEID, MAX_OSM_NODEID}};
if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION)
const auto &turn_path = turn_restriction.turn_path;
restriction_ways[turn_path.From()] = dummy_segment;
restriction_ways[turn_path.To()] = dummy_segment;
if (turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
const auto &way = turn_restriction.AsWayRestriction();
restriction_ways[way.from] = dummy_segment;
restriction_ways[way.to] = dummy_segment;
for (const auto &v : way.via)
const auto &way = turn_path.AsViaWayPath();
for (const auto &via : way.via)
{
restriction_ways[v] = dummy_segment;
restriction_ways[via] = dummy_segment;
}
}
else
{
BOOST_ASSERT(turn_restriction.Type() == RestrictionType::NODE_RESTRICTION);
const auto &node = turn_restriction.AsNodeRestriction();
restriction_ways[node.from] = dummy_segment;
restriction_ways[node.to] = dummy_segment;
}
};
std::for_each(restrictions_list.begin(), restrictions_list.end(), mark_ids);
@ -992,234 +1176,41 @@ void ExtractionContainers::PrepareRestrictions(const ReferencedWays &restriction
return internal;
};
// Way restrictions are comprised of:
// 1. The segment in the from way that intersects with the via path
// 2. All segments that make up the via path
// 3. The segment in the to way that intersects with the via path.
//
// from: [a, b, c, d, e]
// via: [[f, g, h, i, j], [k, l], [m, n, o]]
// to: [p, q, r, s]
//
// First establish the orientation of the from/via intersection by finding which end
// nodes both ways share. From this we can select the from segment.
//
// intersect | from segment | next_connection
// a=f | b,a | f
// a=j | b,a | j
// e=f | e,d | f
// e=j | e,d | j
//
// Use the next connection to inform the orientation of the first via
// way and the intersection between first and second via ways.
//
// next_connection | intersect | via result | next_next_connection
// f | j=k | [f,g,h,i,j] | k
// f | j=l | [f,g,h,i,j] | l
// j | f=k | [j,i,h,g,f] | k
// j | f=l | [j,i,h,g,f] | l
//
// This is continued for the remaining via ways, appending to the via result
//
// The final via/to intersection also uses the next_connection information in a similar fashion.
//
// next_connection | intersect | to_segment
// m | o=p | p,q
// m | o=s | s,r
// o | m=p | p,q
// o | m=s | s,r
//
// The final result is a list of nodes that represent a valid from->via->to path through the
// ways.
//
// E.g. if intersection nodes are a=j, f=l, k=o, m=s
// the result will be {e [d,c,b,a,i,h,g,f,k,n,m] r}
auto const find_way_restriction = [&](const NodesOfWay &from_way,
const std::vector<NodesOfWay> &via_ways,
const NodesOfWay &to_way) {
BOOST_ASSERT(!via_ways.empty());
WayRestriction restriction;
// Find the orientation of the connected ways starting with the from-via intersection.
OSMNodeID from, via;
std::tie(from, via, std::ignore) =
find_turn_nodes(from_way, via_ways.front(), SPECIAL_OSM_NODEID);
if (via == SPECIAL_OSM_NODEID)
{
util::Log(logDEBUG) << "Restriction has unconnected from and via ways: "
<< from_way.way_id << ", " << via_ways.front().way_id;
return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
restriction.from = to_internal(from);
restriction.via.push_back(to_internal(via));
// Use the connection node from the previous intersection to inform our conversion of
// via ways into internal nodes.
OSMNodeID next_connection = via;
for (const auto &via_way : via_ways)
{
if (next_connection == via_way.first_segment_source_id())
{
std::transform(std::next(via_way.node_ids.begin()),
via_way.node_ids.end(),
std::back_inserter(restriction.via),
to_internal);
next_connection = via_way.last_segment_target_id();
}
else if (next_connection == via_way.last_segment_target_id())
{
std::transform(std::next(via_way.node_ids.rbegin()),
via_way.node_ids.rend(),
std::back_inserter(restriction.via),
to_internal);
next_connection = via_way.first_segment_source_id();
}
else
{
util::Log(logDEBUG) << "Restriction has unconnected via way: " << via_way.way_id
<< " to node " << next_connection;
return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
}
// Add the final to node after the via-to intersection.
if (next_connection == to_way.first_segment_source_id())
{
restriction.to = to_internal(to_way.first_segment_target_id());
}
else if (next_connection == to_way.last_segment_target_id())
{
restriction.to = to_internal(to_way.last_segment_source_id());
}
else
{
util::Log(logDEBUG) << "Restriction has unconnected via and to ways: "
<< via_ways.back().way_id << ", " << to_way.way_id;
return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
return restriction;
};
// Check if we were able to resolve all the involved OSM elements before translating to an
// internal restriction
auto const get_way_restriction_from_OSM_ids =
[&](auto const from_id, auto const to_id, const std::vector<OSMWayID> &via_ids) {
auto const from_way_itr = restriction_ways.find(from_id);
if (from_way_itr->second.way_id != from_id)
{
util::Log(logDEBUG) << "Restriction references invalid from way: " << from_id;
return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
std::vector<NodesOfWay> via_ways;
for (const auto &via_id : via_ids)
{
auto const via_segment_itr = restriction_ways.find(via_id);
if (via_segment_itr->second.way_id != via_id)
{
util::Log(logDEBUG) << "Restriction references invalid via way: " << via_id;
return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
via_ways.push_back(via_segment_itr->second);
}
auto const to_way_itr = restriction_ways.find(to_id);
if (to_way_itr->second.way_id != to_id)
{
util::Log(logDEBUG) << "Restriction references invalid to way: " << to_id;
return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
}
return find_way_restriction(from_way_itr->second, via_ways, to_way_itr->second);
};
// Node restrictions are described as a restriction between the two segments closest
// to the shared via-node on the from and to ways.
// from: [a, b, c, d, e]
// to: [f, g, h, i, j]
//
// The via node establishes the orientation of the from/to intersection when choosing the
// segments.
// via | node restriction
// a=f | b,a,g
// a=j | b,a,i
// e=f | d,e,g
// e=j | d,e,i
auto const find_node_restriction =
[&](auto const &from_segment, auto const &to_segment, auto const via_node) {
OSMNodeID from, via, to;
std::tie(from, via, to) = find_turn_nodes(from_segment, to_segment, via_node);
if (via == SPECIAL_OSM_NODEID)
{
// unconnected
util::Log(logDEBUG)
<< "Restriction references unconnected way: " << from_segment.way_id;
return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
return NodeRestriction{to_internal(from), to_internal(via), to_internal(to)};
};
// Check if we were able to resolve all the involved OSM elements before translating to an
// internal restriction
auto const get_node_restriction_from_OSM_ids = [&](auto const from_id,
auto const to_id,
const OSMNodeID via_node) {
auto const from_segment_itr = restriction_ways.find(from_id);
if (from_segment_itr->second.way_id != from_id)
{
util::Log(logDEBUG) << "Restriction references invalid way: " << from_id;
return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
auto const to_segment_itr = restriction_ways.find(to_id);
if (to_segment_itr->second.way_id != to_id)
{
util::Log(logDEBUG) << "Restriction references invalid way: " << to_id;
return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
return find_node_restriction(from_segment_itr->second, to_segment_itr->second, via_node);
};
// Transform an OSMRestriction (based on WayIDs) into an OSRM restriction (base on NodeIDs).
// Returns true on successful transformation, false in case of invalid references.
const auto transform = [&](const auto &external_type, auto &internal_type) {
if (external_type.Type() == RestrictionType::WAY_RESTRICTION)
if (external_type.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
auto const &external = external_type.AsWayRestriction();
auto const restriction =
get_way_restriction_from_OSM_ids(external.from, external.to, external.via);
if (!restriction.Valid())
return false;
internal_type.node_or_way = restriction;
return true;
auto const &external = external_type.turn_path.AsViaWayPath();
internal_type.turn_path.node_or_way =
get_via_way_path_from_OSM_ids(internal_type.Name(),
restriction_ways,
external.from,
external.to,
external.via,
to_internal);
}
else
{
BOOST_ASSERT(external_type.Type() == RestrictionType::NODE_RESTRICTION);
auto const &external = external_type.AsNodeRestriction();
auto restriction =
get_node_restriction_from_OSM_ids(external.from, external.to, external.via);
if (!restriction.Valid())
return false;
internal_type.node_or_way = restriction;
return true;
BOOST_ASSERT(external_type.turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH);
auto const &external = external_type.turn_path.AsViaNodePath();
internal_type.turn_path.node_or_way =
get_via_node_path_from_OSM_ids(internal_type.Name(),
restriction_ways,
external.from,
external.to,
external.via,
to_internal);
}
internal_type.is_only = external_type.is_only;
internal_type.condition = std::move(external_type.condition);
return internal_type.Valid();
};
const auto transform_into_internal_types = [&](InputTurnRestriction &external_restriction) {
TurnRestriction restriction;
if (transform(external_restriction, restriction))
{
restriction.is_only = external_restriction.is_only;
restriction.condition = std::move(external_restriction.condition);
turn_restrictions.push_back(std::move(restriction));
}
};

View File

@ -13,11 +13,11 @@
#include "extractor/name_table.hpp"
#include "extractor/node_based_graph_factory.hpp"
#include "extractor/node_restriction_map.hpp"
#include "extractor/restriction_filter.hpp"
#include "extractor/restriction_graph.hpp"
#include "extractor/restriction_parser.hpp"
#include "extractor/scripting_environment.hpp"
#include "extractor/tarjan_scc.hpp"
#include "extractor/turn_path_filter.hpp"
#include "extractor/way_restriction_map.hpp"
#include "guidance/files.hpp"
@ -279,7 +279,9 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
edge_based_nodes_container =
EdgeBasedNodeDataContainer({}, std::move(node_based_graph_factory.GetAnnotationData()));
turn_restrictions = removeInvalidRestrictions(std::move(turn_restrictions), node_based_graph);
turn_restrictions = removeInvalidTurnPaths(std::move(turn_restrictions), node_based_graph);
unresolved_maneuver_overrides =
removeInvalidTurnPaths(std::move(unresolved_maneuver_overrides), node_based_graph);
auto restriction_graph = constructRestrictionGraph(turn_restrictions);
const auto number_of_node_based_nodes = node_based_graph.GetNumberOfNodes();

View File

@ -3,8 +3,7 @@
#include "extractor/compressed_edge_container.hpp"
#include "extractor/extraction_turn.hpp"
#include "extractor/restriction.hpp"
#include "extractor/restriction_compressor.hpp"
#include "guidance/intersection.hpp"
#include "extractor/turn_path_compressor.hpp"
#include "util/dynamic_graph.hpp"
#include "util/node_based_graph.hpp"
@ -32,28 +31,34 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
const unsigned original_number_of_nodes = graph.GetNumberOfNodes();
const unsigned original_number_of_edges = graph.GetNumberOfEdges();
RestrictionCompressor restriction_compressor(turn_restrictions, maneuver_overrides);
TurnPathCompressor turn_path_compressor(turn_restrictions, maneuver_overrides);
// Some degree two nodes are not compressed if they act as entry/exit points into a
// restriction path.
std::unordered_set<NodeID> restriction_via_nodes;
std::unordered_set<NodeID> incompressible_via_nodes;
const auto remember_via_nodes = [&](const auto &restriction) {
if (restriction.Type() == RestrictionType::NODE_RESTRICTION)
if (restriction.turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH)
{
restriction_via_nodes.insert(restriction.AsNodeRestriction().via);
incompressible_via_nodes.insert(restriction.turn_path.AsViaNodePath().via);
}
else
{
BOOST_ASSERT(restriction.Type() == RestrictionType::WAY_RESTRICTION);
const auto &way_restriction = restriction.AsWayRestriction();
BOOST_ASSERT(restriction.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH);
const auto &way_restriction = restriction.turn_path.AsViaWayPath();
// We do not compress the first and last via nodes so that we know how to enter/exit
// a restriction path and apply the restrictions correctly.
restriction_via_nodes.insert(way_restriction.via.front());
restriction_via_nodes.insert(way_restriction.via.back());
incompressible_via_nodes.insert(way_restriction.via.front());
incompressible_via_nodes.insert(way_restriction.via.back());
}
};
std::for_each(turn_restrictions.begin(), turn_restrictions.end(), remember_via_nodes);
for (const auto &maneuver : maneuver_overrides)
{
// Only incompressible is where the instruction occurs.
incompressible_via_nodes.insert(maneuver.instruction_node);
}
{
const auto weight_multiplier =
scripting_environment.GetProfileProperties().GetWeightMultiplier();
@ -77,7 +82,7 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
}
// check if v is an entry/exit via node for a turn restriction
if (restriction_via_nodes.count(node_v) > 0)
if (incompressible_via_nodes.count(node_v) > 0)
{
continue;
}
@ -303,8 +308,8 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
graph.DeleteEdge(node_v, forward_e2);
graph.DeleteEdge(node_v, reverse_e2);
// update any involved turn restrictions
restriction_compressor.Compress(node_u, node_v, node_w);
// update any involved turn relations
turn_path_compressor.Compress(node_u, node_v, node_w);
// store compressed geometry in container
geometry_compressor.CompressEdge(forward_e1,

View File

@ -119,9 +119,15 @@ ManeuverOverrideRelationParser::TryParse(const osmium::Relation &relation) const
if (valid_relation)
{
maneuver_override.via_ways.push_back(from);
std::copy(via_ways.begin(), via_ways.end(), std::back_inserter(maneuver_override.via_ways));
maneuver_override.via_ways.push_back(to);
if (via_ways.empty())
{
maneuver_override.turn_path.node_or_way = InputViaNodePath{{from}, {via_node}, {to}};
}
else
{
maneuver_override.turn_path.node_or_way =
InputViaWayPath{{from}, std::move(via_ways), {to}};
}
maneuver_override.via_node = via_node;
}
else

View File

@ -1,242 +0,0 @@
#include "extractor/restriction_compressor.hpp"
#include "extractor/restriction.hpp"
#include <algorithm>
#include <boost/assert.hpp>
namespace osrm
{
namespace extractor
{
RestrictionCompressor::RestrictionCompressor(
std::vector<TurnRestriction> &restrictions,
std::vector<UnresolvedManeuverOverride> &maneuver_overrides)
{
// add a turn restriction ptr to the starts/ends maps, needs to be a reference!
auto index = [&](auto &element) {
starts.insert({element.From(), &element});
ends.insert({element.To(), &element});
if (element.Type() == RestrictionType::WAY_RESTRICTION)
{
const auto &way_via = element.AsWayRestriction().via;
BOOST_ASSERT(way_via.size() >= 2);
// No need to track the first and last via nodes as they will not be compressed.
for (const auto &via_node :
boost::make_iterator_range(way_via.begin() + 1, way_via.end() - 1))
{
vias.insert({via_node, &element});
}
}
};
// !needs to be reference, so we can get the correct address
const auto index_starts_ends_vias = [&](auto &restriction) { index(restriction); };
// add all restrictions as their respective start/via/end pointers
std::for_each(restrictions.begin(), restrictions.end(), index_starts_ends_vias);
auto index_maneuver = [&](auto &maneuver) {
for (auto &turn : maneuver.turn_sequence)
{
maneuver_starts.insert({turn.from, &turn});
maneuver_ends.insert({turn.to, &turn});
}
};
// !needs to be reference, so we can get the correct address
std::for_each(maneuver_overrides.begin(), maneuver_overrides.end(), [&](auto &maneuver) {
index_maneuver(maneuver);
});
}
void RestrictionCompressor::Compress(const NodeID from, const NodeID via, const NodeID to)
{
// handle turn restrictions
// extract all start ptrs and move them from via to from.
auto all_starts_range = starts.equal_range(via);
std::vector<TurnRestriction *> start_ptrs;
std::transform(all_starts_range.first,
all_starts_range.second,
std::back_inserter(start_ptrs),
[](const auto pair) { return pair.second; });
const auto update_start = [&](auto ptr) {
if (ptr->Type() == RestrictionType::NODE_RESTRICTION)
{
// ____ | from - p.from | via - p.via | to - p.to | ____
auto &node_ptr = ptr->AsNodeRestriction();
BOOST_ASSERT(node_ptr.from == via);
if (node_ptr.via == to)
{
node_ptr.from = from;
}
// ____ | to - p.from | via - p.via | from - p.to | ____
else
{
BOOST_ASSERT(node_ptr.via == from);
node_ptr.from = to;
}
}
else
{
BOOST_ASSERT(ptr->Type() == RestrictionType::WAY_RESTRICTION);
auto &way_ptr = ptr->AsWayRestriction();
// ____ | from - p.from | via - p.via[0] | to - p[1..],p.to | ____
BOOST_ASSERT(way_ptr.from == via);
if (way_ptr.via.front() == to)
{
way_ptr.from = from;
}
// ____ | to - p.from | via - p.via[0] | from - p[1,..],p.to | ____
else
{
BOOST_ASSERT(way_ptr.via.front() == from);
way_ptr.from = to;
}
}
};
std::for_each(start_ptrs.begin(), start_ptrs.end(), update_start);
// update the ptrs in our mapping
starts.erase(via);
const auto reinsert_start = [&](auto ptr) { starts.insert({ptr->From(), ptr}); };
std::for_each(start_ptrs.begin(), start_ptrs.end(), reinsert_start);
// extract all end ptrs and move them from via to to
auto all_ends_range = ends.equal_range(via);
std::vector<TurnRestriction *> end_ptrs;
std::transform(all_ends_range.first,
all_ends_range.second,
std::back_inserter(end_ptrs),
[](const auto pair) { return pair.second; });
const auto update_end = [&](auto ptr) {
if (ptr->Type() == RestrictionType::NODE_RESTRICTION)
{
auto &node_ptr = ptr->AsNodeRestriction();
BOOST_ASSERT(node_ptr.to == via);
// p.from | ____ - p.via | from - p.to | via - ____ | to
if (node_ptr.via == from)
{
node_ptr.to = to;
}
// p.from | ____ - p.via | to - p.to | via - ____ | from
else
{
BOOST_ASSERT(node_ptr.via == to);
node_ptr.to = from;
}
}
else
{
BOOST_ASSERT(ptr->Type() == RestrictionType::WAY_RESTRICTION);
auto &way_ptr = ptr->AsWayRestriction();
BOOST_ASSERT(way_ptr.to == via);
// p.from,p.via[..,n-1] | ____ - p.via[n] | from - p.to | via - ____ | to
if (way_ptr.via.back() == from)
{
way_ptr.to = to;
}
// p.from,p.via[..,n-1] | ____ - p.via[n] | to - p.to | via - ____ | from
else
{
BOOST_ASSERT(way_ptr.via.back() == to);
way_ptr.to = from;
}
}
};
std::for_each(end_ptrs.begin(), end_ptrs.end(), update_end);
// update end ptrs in mapping
ends.erase(via);
const auto reinsert_end = [&](auto ptr) { ends.insert({ptr->To(), ptr}); };
std::for_each(end_ptrs.begin(), end_ptrs.end(), reinsert_end);
// remove compressed node from all via paths
auto all_vias_range = vias.equal_range(via);
const auto update_via = [&](auto restriction_pair) {
BOOST_ASSERT(restriction_pair.second->Type() == RestrictionType::WAY_RESTRICTION);
auto &way_ptr = restriction_pair.second->AsWayRestriction();
BOOST_ASSERT(std::find(way_ptr.via.begin(), way_ptr.via.end(), via) != way_ptr.via.end());
way_ptr.via.erase(std::remove(way_ptr.via.begin(), way_ptr.via.end(), via),
way_ptr.via.end());
};
std::for_each(all_vias_range.first, all_vias_range.second, update_via);
// update via ptrs in mapping
vias.erase(via);
/**********************************************************************************************/
// handle maneuver overrides from nodes
// extract all startptrs
auto maneuver_starts_range = maneuver_starts.equal_range(via);
std::vector<NodeBasedTurn *> mnv_start_ptrs;
std::transform(maneuver_starts_range.first,
maneuver_starts_range.second,
std::back_inserter(mnv_start_ptrs),
[](const auto pair) { return pair.second; });
// update from nodes of maneuver overrides
const auto update_start_mnv = [&](auto ptr) {
// ____ | from - p.from | via - p.via | to - p.to | ____
BOOST_ASSERT(ptr->from == via);
if (ptr->via == to)
{
ptr->from = from;
}
// ____ | to - p.from | via - p.via | from - p.to | ____
else
{
BOOST_ASSERT(ptr->via == from);
ptr->from = to;
}
};
std::for_each(mnv_start_ptrs.begin(), mnv_start_ptrs.end(), update_start_mnv);
// update the ptrs in our mapping
maneuver_starts.erase(via);
const auto reinsert_start_mnv = [&](auto ptr) { maneuver_starts.insert({ptr->from, ptr}); };
std::for_each(mnv_start_ptrs.begin(), mnv_start_ptrs.end(), reinsert_start_mnv);
/**********************************************************************************************/
// handle maneuver override to nodes
// extract all end ptrs and move them from via to to
auto maneuver_ends_range = maneuver_ends.equal_range(via);
std::vector<NodeBasedTurn *> mnv_end_ptrs;
std::transform(maneuver_ends_range.first,
maneuver_ends_range.second,
std::back_inserter(mnv_end_ptrs),
[](const auto pair) { return pair.second; });
const auto update_end_mnv = [&](auto ptr) {
BOOST_ASSERT(ptr->to == via);
// p.from | ____ - p.via | from - p.to | via - ____ | to
if (ptr->via == from)
{
ptr->to = to;
}
// p.from | ____ - p.via | to - p.to | via - ____ | from
else
{
BOOST_ASSERT(ptr->via == to);
ptr->to = from;
}
};
std::for_each(mnv_end_ptrs.begin(), mnv_end_ptrs.end(), update_end_mnv);
// update end ptrs in mapping
maneuver_ends.erase(via);
const auto reinsert_end_mnvs = [&](auto ptr) { maneuver_ends.insert({ptr->to, ptr}); };
std::for_each(mnv_end_ptrs.begin(), mnv_end_ptrs.end(), reinsert_end_mnvs);
}
} // namespace extractor
} // namespace osrm

View File

@ -1,83 +0,0 @@
#include "extractor/restriction_filter.hpp"
#include "util/node_based_graph.hpp"
#include "util/timing_util.hpp"
#include "util/typedefs.hpp"
#include <algorithm>
#include <boost/assert.hpp>
namespace osrm
{
namespace extractor
{
std::vector<TurnRestriction>
removeInvalidRestrictions(std::vector<TurnRestriction> restrictions,
const util::NodeBasedDynamicGraph &node_based_graph)
{
util::UnbufferedLog log;
log << "Removing invalid restrictions...";
TIMER_START(remove_invalid_restrictions);
const auto is_valid_edge = [&node_based_graph](const auto from, const auto to) {
const auto eid = node_based_graph.FindEdge(from, to);
if (eid == SPECIAL_EDGEID)
{
util::Log(logDEBUG) << "Restriction has invalid edge: " << from << ", " << to;
return false;
}
const auto &edge_data = node_based_graph.GetEdgeData(eid);
if (edge_data.reversed)
{
util::Log(logDEBUG) << "Restriction has non-traversable edge: " << from << ", " << to;
return false;
}
return true;
};
const auto is_valid_node = [is_valid_edge](const auto &node_restriction) {
return is_valid_edge(node_restriction.from, node_restriction.via) &&
is_valid_edge(node_restriction.via, node_restriction.to);
};
const auto is_valid_way = [is_valid_edge](const auto &way_restriction) {
if (!is_valid_edge(way_restriction.from, way_restriction.via.front()))
return false;
const auto invalid_it = std::adjacent_find(
way_restriction.via.begin(),
way_restriction.via.end(),
[&](auto via_from, auto via_to) { return !is_valid_edge(via_from, via_to); });
if (invalid_it != way_restriction.via.end())
return false;
return is_valid_edge(way_restriction.via.back(), way_restriction.to);
};
const auto is_invalid = [is_valid_way, is_valid_node](const auto &restriction) {
if (restriction.Type() == RestrictionType::NODE_RESTRICTION)
{
return !is_valid_node(restriction.AsNodeRestriction());
}
else
{
BOOST_ASSERT(restriction.Type() == RestrictionType::WAY_RESTRICTION);
return !is_valid_way(restriction.AsWayRestriction());
}
};
const auto end_valid_restrictions =
std::remove_if(restrictions.begin(), restrictions.end(), is_invalid);
const auto num_removed = std::distance(end_valid_restrictions, restrictions.end());
restrictions.erase(end_valid_restrictions, restrictions.end());
TIMER_STOP(remove_invalid_restrictions);
log << "removed " << num_removed << " invalid restrictions, after "
<< TIMER_SEC(remove_invalid_restrictions) << "s";
return restrictions;
}
} // namespace extractor
} // namespace osrm

View File

@ -1,9 +1,10 @@
#include "extractor/restriction_graph.hpp"
#include "extractor/restriction.hpp"
#include "extractor/turn_path.hpp"
#include "util/node_based_graph.hpp"
#include "util/timing_util.hpp"
#include <util/for_each_pair.hpp>
#include <boost/assign.hpp>
#include <boost/range/algorithm/copy.hpp>
namespace osrm
@ -211,11 +212,11 @@ void buildGraph(RestrictionGraph &rg, const std::vector<TurnRestriction> &restri
const auto run_builder = [&](const auto &restriction) {
builder_type builder(rg);
builder.start(restriction.From(), restriction.FirstVia());
if (restriction.Type() == RestrictionType::WAY_RESTRICTION)
builder.start(restriction.turn_path.From(), restriction.turn_path.FirstVia());
if (restriction.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
const auto &way_restriction = restriction.AsWayRestriction();
util::for_each_pair(way_restriction.via,
const auto &via_way_path = restriction.turn_path.AsViaWayPath();
util::for_each_pair(via_way_path.via,
[&](NodeID from, NodeID to) { builder.next(from, to); });
}
builder.end(restriction);

View File

@ -232,7 +232,7 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
return {};
}
// Internally restrictions are represented with one 'from' and one 'to' way.
// Therefore we need to convert a multi from/to restriction into multiple restrictions.
// Therefore, we need to convert a multi from/to restriction into multiple restrictions.
for (const auto &from : from_ways)
{
for (const auto &to : to_ways)
@ -243,12 +243,12 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
if (is_node_restriction)
{
// template struct requires bracket for ID initialisation :(
restriction.node_or_way = InputNodeRestriction{{from}, {via_node}, {to}};
restriction.turn_path.node_or_way = InputViaNodePath{{from}, {via_node}, {to}};
}
else
{
// template struct requires bracket for ID initialisation :(
restriction.node_or_way = InputWayRestriction{{from}, via_ways, {to}};
restriction.turn_path.node_or_way = InputViaWayPath{{from}, via_ways, {to}};
}
restriction_containers.push_back(std::move(restriction));
}

View File

@ -0,0 +1,164 @@
#include "extractor/turn_path_compressor.hpp"
#include "extractor/maneuver_override.hpp"
#include "extractor/restriction.hpp"
#include <algorithm>
#include <boost/assert.hpp>
namespace osrm
{
namespace extractor
{
TurnPathCompressor::TurnPathCompressor(std::vector<TurnRestriction> &restrictions,
std::vector<UnresolvedManeuverOverride> &maneuver_overrides)
{
// Track all turn paths by their respective start/via/end nodes.
auto index = [&](auto &element) {
starts.insert({element.From(), &element});
ends.insert({element.To(), &element});
if (element.Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
// Some of the via nodes can not be compressed so don't need tracking
// (e.g. first and last via node of a restriction, instruction node of maneuver).
// However, for the sake of simplicity we'll track them all.
for (const auto &via_node : element.AsViaWayPath().via)
{
vias.insert({via_node, &element});
}
}
};
// We need to pass a reference to the index as we will mutate the turn path during compression.
const auto index_starts_ends_vias = [&](auto &relation) { index(relation.turn_path); };
std::for_each(restrictions.begin(), restrictions.end(), index_starts_ends_vias);
std::for_each(maneuver_overrides.begin(), maneuver_overrides.end(), index_starts_ends_vias);
}
void TurnPathCompressor::Compress(const NodeID from, const NodeID via, const NodeID to)
{
// handle turn restrictions
// extract all start ptrs and move them from via to from.
auto all_starts_range = starts.equal_range(via);
std::vector<TurnPath *> start_ptrs;
std::transform(all_starts_range.first,
all_starts_range.second,
std::back_inserter(start_ptrs),
[](const auto pair) { return pair.second; });
const auto update_start = [&](auto ptr) {
if (ptr->Type() == TurnPathType::VIA_NODE_TURN_PATH)
{
// ____ | from - p.from | via - p.via | to - p.to | ____
auto &node_ptr = ptr->AsViaNodePath();
BOOST_ASSERT(node_ptr.from == via);
if (node_ptr.via == to)
{
node_ptr.from = from;
}
// ____ | to - p.from | via - p.via | from - p.to | ____
else
{
BOOST_ASSERT(node_ptr.via == from);
node_ptr.from = to;
}
}
else
{
BOOST_ASSERT(ptr->Type() == TurnPathType::VIA_WAY_TURN_PATH);
auto &way_ptr = ptr->AsViaWayPath();
// ____ | from - p.from | via - p.via[0] | to - p[1..],p.to | ____
BOOST_ASSERT(way_ptr.from == via);
if (way_ptr.via.front() == to)
{
way_ptr.from = from;
}
// ____ | to - p.from | via - p.via[0] | from - p[1,..],p.to | ____
else
{
BOOST_ASSERT(way_ptr.via.front() == from);
way_ptr.from = to;
}
}
};
std::for_each(start_ptrs.begin(), start_ptrs.end(), update_start);
// update the ptrs in our mapping
starts.erase(via);
const auto reinsert_start = [&](auto ptr) { starts.insert({ptr->From(), ptr}); };
std::for_each(start_ptrs.begin(), start_ptrs.end(), reinsert_start);
// extract all end ptrs and move them from via to to
auto all_ends_range = ends.equal_range(via);
std::vector<TurnPath *> end_ptrs;
std::transform(all_ends_range.first,
all_ends_range.second,
std::back_inserter(end_ptrs),
[](const auto pair) { return pair.second; });
const auto update_end = [&](auto ptr) {
if (ptr->Type() == TurnPathType::VIA_NODE_TURN_PATH)
{
auto &node_ptr = ptr->AsViaNodePath();
BOOST_ASSERT(node_ptr.to == via);
// p.from | ____ - p.via | from - p.to | via - ____ | to
if (node_ptr.via == from)
{
node_ptr.to = to;
}
// p.from | ____ - p.via | to - p.to | via - ____ | from
else
{
BOOST_ASSERT(node_ptr.via == to);
node_ptr.to = from;
}
}
else
{
BOOST_ASSERT(ptr->Type() == TurnPathType::VIA_WAY_TURN_PATH);
auto &way_ptr = ptr->AsViaWayPath();
BOOST_ASSERT(way_ptr.to == via);
// p.from,p.via[..,n-1] | ____ - p.via[n] | from - p.to | via - ____ | to
if (way_ptr.via.back() == from)
{
way_ptr.to = to;
}
// p.from,p.via[..,n-1] | ____ - p.via[n] | to - p.to | via - ____ | from
else
{
BOOST_ASSERT(way_ptr.via.back() == to);
way_ptr.to = from;
}
}
};
std::for_each(end_ptrs.begin(), end_ptrs.end(), update_end);
// update end ptrs in mapping
ends.erase(via);
const auto reinsert_end = [&](auto ptr) { ends.insert({ptr->To(), ptr}); };
std::for_each(end_ptrs.begin(), end_ptrs.end(), reinsert_end);
// remove compressed node from all via paths
auto all_vias_range = vias.equal_range(via);
const auto update_via = [&](auto restriction_pair) {
BOOST_ASSERT(restriction_pair.second->Type() == TurnPathType::VIA_WAY_TURN_PATH);
auto &way_ptr = restriction_pair.second->AsViaWayPath();
BOOST_ASSERT(std::find(way_ptr.via.begin(), way_ptr.via.end(), via) != way_ptr.via.end());
way_ptr.via.erase(std::remove(way_ptr.via.begin(), way_ptr.via.end(), via),
way_ptr.via.end());
};
std::for_each(all_vias_range.first, all_vias_range.second, update_via);
// update via ptrs in mapping
vias.erase(via);
}
} // namespace extractor
} // namespace osrm

View File

@ -0,0 +1,88 @@
#include "extractor/turn_path_filter.hpp"
#include "extractor/maneuver_override.hpp"
#include "util/node_based_graph.hpp"
#include "util/timing_util.hpp"
#include "util/typedefs.hpp"
#include <algorithm>
#include <boost/assert.hpp>
namespace osrm
{
namespace extractor
{
template <typename T>
std::vector<T> removeInvalidTurnPaths(std::vector<T> turn_relations,
const util::NodeBasedDynamicGraph &node_based_graph)
{
util::UnbufferedLog log;
log << "Removing invalid " << T::Name() << "s...";
TIMER_START(remove_invalid_turn_paths);
const auto is_valid_edge = [&node_based_graph](const auto from, const auto to) {
const auto eid = node_based_graph.FindEdge(from, to);
if (eid == SPECIAL_EDGEID)
{
util::Log(logDEBUG) << T::Name() << " has invalid edge: " << from << ", " << to;
return false;
}
const auto &edge_data = node_based_graph.GetEdgeData(eid);
if (edge_data.reversed)
{
util::Log(logDEBUG) << T::Name() << " has non-traversable edge: " << from << ", " << to;
return false;
}
return true;
};
const auto is_valid_node = [is_valid_edge](const auto &via_node_path) {
return is_valid_edge(via_node_path.from, via_node_path.via) &&
is_valid_edge(via_node_path.via, via_node_path.to);
};
const auto is_valid_way = [is_valid_edge](const auto &via_way_path) {
if (!is_valid_edge(via_way_path.from, via_way_path.via.front()))
return false;
const auto invalid_it = std::adjacent_find(
via_way_path.via.begin(), via_way_path.via.end(), [&](auto via_from, auto via_to) {
return !is_valid_edge(via_from, via_to);
});
if (invalid_it != via_way_path.via.end())
return false;
return is_valid_edge(via_way_path.via.back(), via_way_path.to);
};
const auto is_invalid = [is_valid_way, is_valid_node](const auto &turn_relation) {
if (turn_relation.turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH)
{
return !is_valid_node(turn_relation.turn_path.AsViaNodePath());
}
BOOST_ASSERT(turn_relation.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH);
return !is_valid_way(turn_relation.turn_path.AsViaWayPath());
};
const auto end_valid_relations =
std::remove_if(turn_relations.begin(), turn_relations.end(), is_invalid);
const auto num_removed = std::distance(end_valid_relations, turn_relations.end());
turn_relations.erase(end_valid_relations, turn_relations.end());
TIMER_STOP(remove_invalid_turn_paths);
log << "removed " << num_removed << " invalid " << T::Name() << "s, after "
<< TIMER_SEC(remove_invalid_turn_paths) << "s";
return turn_relations;
}
template std::vector<TurnRestriction> removeInvalidTurnPaths<>(std::vector<TurnRestriction>,
const util::NodeBasedDynamicGraph &);
template std::vector<UnresolvedManeuverOverride>
removeInvalidTurnPaths<>(std::vector<UnresolvedManeuverOverride>,
const util::NodeBasedDynamicGraph &);
} // namespace extractor
} // namespace osrm

View File

@ -23,7 +23,7 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
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<TurnRestriction> restrictions{TurnRestriction{{ViaNodePath{0, 2, 1}}, false}};
CompressedEdgeContainer container;
test::MockScriptingEnvironment scripting_environment;
std::vector<UnresolvedManeuverOverride> maneuver_overrides;

View File

@ -15,14 +15,14 @@ using namespace osrm::extractor;
TurnRestriction makeWayRestriction(NodeID from, std::vector<NodeID> via, NodeID to, bool is_only)
{
WayRestriction wr{from, std::move(via), to};
return TurnRestriction(wr, is_only);
ViaWayPath vwp{from, std::move(via), to};
return TurnRestriction({vwp}, is_only);
}
TurnRestriction makeNodeRestriction(NodeID from, NodeID via, NodeID to, bool is_only)
{
NodeRestriction nr{from, via, to};
return TurnRestriction(nr, is_only);
ViaNodePath vnp{from, via, to};
return TurnRestriction({vnp}, is_only);
}
struct instruction
@ -52,7 +52,7 @@ void checkInstructions(RestrictionGraph::RestrictionRange restrictions,
restrictions.end(),
std::back_inserter(actual_instructions),
[](const auto &restriction) {
return instruction{restriction->To(), bool(restriction->is_only)};
return instruction{restriction->turn_path.To(), bool(restriction->is_only)};
});
std::sort(actual_instructions.begin(),
actual_instructions.end(),