refactor restriction parsing / extraction to actual types

Makes turn restrictions into dedicated structures and diferentiates between them via a variant.
Ensures that we do not accidentally mess up ID types within our application.
In addition this improves the restriction performance by only parsing all edges
once at the cost of (at the time of writing) 22MB in terms of main memory usage.
This commit is contained in:
Moritz Kobitzsch 2017-07-06 17:09:24 +02:00
parent 1f7aa6f812
commit 2e9a7d9c1a
21 changed files with 876 additions and 461 deletions

View File

@ -2,7 +2,6 @@
#define EXTRACTION_CONTAINERS_HPP
#include "extractor/first_and_last_segment_of_way.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/internal_extractor_edge.hpp"
#include "extractor/query_node.hpp"
#include "extractor/restriction.hpp"
@ -28,7 +27,7 @@ class ExtractionContainers
void PrepareEdges(ScriptingEnvironment &scripting_environment);
void WriteNodes(storage::io::FileWriter &file_out) const;
void WriteRestrictions(const std::string &restrictions_file_name);
void WriteConditionalRestrictions(const std::string &restrictions_file_name);
void WriteEdges(storage::io::FileWriter &file_out) const;
void WriteCharData(const std::string &file_name);
@ -36,7 +35,6 @@ class ExtractionContainers
using NodeIDVector = std::vector<OSMNodeID>;
using NodeVector = std::vector<QueryNode>;
using EdgeVector = std::vector<InternalExtractorEdge>;
using RestrictionsVector = std::vector<InputRestrictionContainer>;
using WayIDStartEndVector = std::vector<FirstAndLastSegmentOfWay>;
using NameCharData = std::vector<unsigned char>;
using NameOffsets = std::vector<unsigned>;
@ -49,9 +47,15 @@ class ExtractionContainers
NameCharData name_char_data;
NameOffsets name_offsets;
// an adjacency array containing all turn lane masks
RestrictionsVector restrictions_list;
WayIDStartEndVector way_start_end_id_list;
unsigned max_internal_node_id;
// list of restrictions before we transform them into the output types
std::vector<InputConditionalTurnRestriction> restrictions_list;
// turn restrictions split into conditional and unconditional turn restrictions
std::vector<ConditionalTurnRestriction> conditional_turn_restrictions;
std::vector<TurnRestriction> unconditional_turn_restrictions;
ExtractionContainers();

View File

@ -42,10 +42,10 @@ namespace extractor
{
class ExtractionContainers;
struct InputRestrictionContainer;
struct ExtractionNode;
struct ExtractionWay;
struct ProfileProperties;
struct InputConditionalTurnRestriction;
/**
* This class is used by the extractor with the results of the
@ -83,7 +83,7 @@ class ExtractorCallbacks
void ProcessNode(const osmium::Node &current_node, const ExtractionNode &result_node);
// warning: caller needs to take care of synchronization!
void ProcessRestriction(const boost::optional<InputRestrictionContainer> &restriction);
void ProcessRestriction(const InputConditionalTurnRestriction &restriction);
// warning: caller needs to take care of synchronization!
void ProcessWay(const osmium::Way &current_way, const ExtractionWay &result_way);

View File

@ -7,6 +7,7 @@
#include <memory>
#include <unordered_set>
#include <vector>
namespace osrm
{
@ -14,7 +15,7 @@ namespace extractor
{
class CompressedEdgeContainer;
class RestrictionMap;
struct TurnRestriction;
class GraphCompressor
{
@ -23,7 +24,7 @@ class GraphCompressor
public:
void Compress(const std::unordered_set<NodeID> &barrier_nodes,
const std::unordered_set<NodeID> &traffic_lights,
RestrictionMap &restriction_map,
std::vector<TurnRestriction> &turn_restrictions,
util::NodeBasedDynamicGraph &graph,
CompressedEdgeContainer &geometry_compressor);

View File

@ -5,6 +5,7 @@
#include "util/opening_hours.hpp"
#include "util/typedefs.hpp"
#include <boost/variant.hpp>
#include <limits>
namespace osrm
@ -12,104 +13,254 @@ 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
{
OSMEdgeID_weak from;
OSMNodeID_weak via;
OSMEdgeID_weak to;
};
// A restriction that uses a single via-way in between
//
// f - e - d
// |
// a - b - c
//
// ab via be to ef -- no u turn
struct InputWayRestriction
{
OSMEdgeID_weak from;
OSMEdgeID_weak via;
OSMEdgeID_weak 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
};
namespace restriction_details
{
// currently these bits only hold an `is_only` value.
struct Bits
{ // mostly unused, initialised to false by default
Bits() : is_only(false) {}
bool is_only;
// when adding more bits, consider using bitfields just as in
// bool unused : 7;
};
} // namespace restriction
struct InputTurnRestriction
{
// keep in the same order as the turn restrictions below
boost::variant<InputNodeRestriction, InputWayRestriction> node_or_way;
restriction_details::Bits flags;
OSMEdgeID_weak From() const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? boost::get<InputNodeRestriction>(node_or_way).from
: boost::get<InputWayRestriction>(node_or_way).from;
}
OSMEdgeID_weak To() const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? boost::get<InputNodeRestriction>(node_or_way).to
: boost::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 boost::get<InputWayRestriction>(node_or_way);
}
const InputWayRestriction &AsWayRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return boost::get<InputWayRestriction>(node_or_way);
}
InputNodeRestriction &AsNodeRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return boost::get<InputNodeRestriction>(node_or_way);
}
const InputNodeRestriction &AsNodeRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return boost::get<InputNodeRestriction>(node_or_way);
}
};
struct InputConditionalTurnRestriction : InputTurnRestriction
{
std::vector<util::OpeningHours> condition;
};
// 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!
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;
};
std::string ToString() const
{
return "From " + std::to_string(from) + " via " + std::to_string(via) + " to " +
std::to_string(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
struct WayRestriction
{
// a way restriction in OSRM is essentially a dual node turn restriction;
//
// | |
// c -x- b
// | |
// d a
//
// from ab via bxc to cd: no_uturn
//
// Technically, we would need only a,b,c,d to describe the full turn in terms of nodes. When
// parsing the relation, though, we do not know about the final representation in the node-based
// graph for the restriction. In case of a traffic light, for example, we might end up with bxc
// not being compressed to bc. For that reason, we need to maintain two node restrictions in
// case a way restrction is not fully collapsed
NodeRestriction in_restriction;
NodeRestriction out_restriction;
};
// Wrapper for turn restrictions that gives more information on its type / handles the switch
// between node/way/multi-way restrictions
struct TurnRestriction
{
union WayOrNode {
OSMNodeID_weak node;
OSMEdgeID_weak way;
};
WayOrNode via;
WayOrNode from;
WayOrNode to;
// keep in the same order as the turn restrictions above
boost::variant<NodeRestriction, WayRestriction> node_or_way;
restriction_details::Bits flags;
std::vector<util::OpeningHours> condition;
struct Bits
{ // mostly unused
Bits()
: is_only(false), uses_via_way(false), unused2(false), unused3(false), unused4(false),
unused5(false), unused6(false), unused7(false)
{
}
bool is_only : 1;
bool uses_via_way : 1;
bool unused2 : 1;
bool unused3 : 1;
bool unused4 : 1;
bool unused5 : 1;
bool unused6 : 1;
bool unused7 : 1;
} flags;
explicit TurnRestriction(NodeID node)
// construction for NodeRestrictions
explicit TurnRestriction(NodeRestriction node_restriction, bool is_only = false)
: node_or_way(node_restriction)
{
via.node = node;
from.node = SPECIAL_NODEID;
to.node = SPECIAL_NODEID;
}
explicit TurnRestriction(const bool is_only = false)
{
via.node = SPECIAL_NODEID;
from.node = SPECIAL_NODEID;
to.node = SPECIAL_NODEID;
flags.is_only = is_only;
}
};
/**
* This is just a wrapper around TurnRestriction used in the extractor.
*
* Could be merged with TurnRestriction. For now the type-destiction makes sense
* as the format in which the restriction is presented in the extractor and in the
* preprocessing is different. (see restriction_parser.cpp)
*/
struct InputRestrictionContainer
{
TurnRestriction restriction;
InputRestrictionContainer(EdgeID fromWay, EdgeID toWay, EdgeID vw)
// construction for WayRestrictions
explicit TurnRestriction(WayRestriction way_restriction, bool is_only = false)
: node_or_way(way_restriction)
{
restriction.from.way = fromWay;
restriction.to.way = toWay;
restriction.via.way = vw;
}
explicit InputRestrictionContainer(bool is_only = false)
{
restriction.from.way = SPECIAL_EDGEID;
restriction.to.way = SPECIAL_EDGEID;
restriction.via.node = SPECIAL_NODEID;
restriction.flags.is_only = is_only;
flags.is_only = is_only;
}
static InputRestrictionContainer min_value() { return InputRestrictionContainer(0, 0, 0); }
static InputRestrictionContainer max_value()
explicit TurnRestriction()
{
return InputRestrictionContainer(SPECIAL_EDGEID, SPECIAL_EDGEID, SPECIAL_EDGEID);
node_or_way = NodeRestriction{SPECIAL_EDGEID, SPECIAL_NODEID, SPECIAL_EDGEID};
}
WayRestriction &AsWayRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return boost::get<WayRestriction>(node_or_way);
}
const WayRestriction &AsWayRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return boost::get<WayRestriction>(node_or_way);
}
NodeRestriction &AsNodeRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return boost::get<NodeRestriction>(node_or_way);
}
const NodeRestriction &AsNodeRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return boost::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.in_restriction.Valid() && restriction.out_restriction.Valid();
}
else
{
auto const &restriction = AsNodeRestriction();
return restriction.Valid();
}
}
std::string ToString() const
{
std::string representation;
if (node_or_way.which() == RestrictionType::WAY_RESTRICTION)
{
auto const &way = AsWayRestriction();
representation =
"In: " + way.in_restriction.ToString() + " Out: " + way.out_restriction.ToString();
}
else
{
auto const &node = AsNodeRestriction();
representation = node.ToString();
}
representation += " is_only: " + std::to_string(flags.is_only);
return representation;
}
};
struct CmpRestrictionContainerByFrom
struct ConditionalTurnRestriction : TurnRestriction
{
using value_type = InputRestrictionContainer;
bool operator()(const InputRestrictionContainer &a, const InputRestrictionContainer &b) const
{
return a.restriction.from.way < b.restriction.from.way;
}
value_type max_value() const { return InputRestrictionContainer::max_value(); }
value_type min_value() const { return InputRestrictionContainer::min_value(); }
};
struct CmpRestrictionContainerByTo
{
using value_type = InputRestrictionContainer;
bool operator()(const InputRestrictionContainer &a, const InputRestrictionContainer &b) const
{
return a.restriction.to.way < b.restriction.to.way;
}
value_type max_value() const { return InputRestrictionContainer::max_value(); }
value_type min_value() const { return InputRestrictionContainer::min_value(); }
std::vector<util::OpeningHours> condition;
};
}
}

View File

@ -0,0 +1,46 @@
#ifndef OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_
#define OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_
#include "util/typedefs.hpp"
#include <boost/unordered_map.hpp>
#include <vector>
namespace osrm
{
namespace extractor
{
struct NodeRestriction;
struct TurnRestriction;
// OSRM stores restrictions in the form 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);
// 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 head via node to tail`. 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. Way turn restrictions are represented by two
// node-restrictions, so we can focus on them alone
boost::unordered_multimap<NodeID, NodeRestriction *> heads;
boost::unordered_multimap<NodeID, NodeRestriction *> tails;
};
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_

View File

@ -69,6 +69,7 @@ namespace osrm
{
namespace extractor
{
/**
\brief Efficent look up if an edge is the start + via node of a TurnRestriction
EdgeBasedEdgeFactory decides by it if edges are inserted or geometry is compressed

View File

@ -3,8 +3,7 @@
#include "extractor/restriction.hpp"
#include <boost/optional/optional.hpp>
#include <boost/optional.hpp>
#include <string>
#include <vector>
@ -38,13 +37,15 @@ class ScriptingEnvironment;
* ...----(a)-----(via)------(b)----...
* So it can be represented by the tripe (a, via, b).
*/
class RestrictionParser
{
public:
RestrictionParser(bool use_turn_restrictions,
bool parse_conditionals,
std::vector<std::string> &restrictions);
std::vector<InputRestrictionContainer> TryParse(const osmium::Relation &relation) const;
boost::optional<InputConditionalTurnRestriction>
TryParse(const osmium::Relation &relation) const;
private:
bool ShouldIgnoreRestriction(const std::string &except_tag_string) const;

View File

@ -57,12 +57,12 @@ class ScriptingEnvironment
virtual void ProcessTurn(ExtractionTurn &turn) = 0;
virtual void ProcessSegment(ExtractionSegment &segment) = 0;
virtual void ProcessElements(
const osmium::memory::Buffer &buffer,
const RestrictionParser &restriction_parser,
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions) = 0;
virtual void
ProcessElements(const osmium::memory::Buffer &buffer,
const RestrictionParser &restriction_parser,
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
std::vector<InputConditionalTurnRestriction> &resulting_restrictions) = 0;
};
}
}

View File

@ -67,12 +67,12 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment
void ProcessTurn(ExtractionTurn &turn) override;
void ProcessSegment(ExtractionSegment &segment) override;
void ProcessElements(
const osmium::memory::Buffer &buffer,
const RestrictionParser &restriction_parser,
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions) override;
void
ProcessElements(const osmium::memory::Buffer &buffer,
const RestrictionParser &restriction_parser,
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
std::vector<InputConditionalTurnRestriction> &resulting_restrictions) override;
private:
void InitContext(LuaScriptingContext &context);

View File

@ -137,41 +137,67 @@ inline void write(storage::io::FileWriter &writer,
storage::serialization::write(writer, node_data_container.classes);
}
// read/write for conditional turn restrictions file
inline void read(storage::io::FileReader &reader, std::vector<TurnRestriction> &restrictions)
inline void read(storage::io::FileReader &reader, NodeRestriction &restriction)
{
auto num_indices = reader.ReadElementCount64();
restrictions.reserve(num_indices);
TurnRestriction restriction;
while (num_indices > 0)
{
bool is_only;
reader.ReadInto(restriction.via);
reader.ReadInto(restriction.from);
reader.ReadInto(restriction.to);
reader.ReadInto(is_only);
auto num_conditions = reader.ReadElementCount64();
restriction.condition.resize(num_conditions);
for (uint64_t i = 0; i < num_conditions; i++)
{
reader.ReadInto(restriction.condition[i].modifier);
storage::serialization::read(reader, restriction.condition[i].times);
storage::serialization::read(reader, restriction.condition[i].weekdays);
storage::serialization::read(reader, restriction.condition[i].monthdays);
}
restriction.flags.is_only = is_only;
reader.ReadInto(restriction.from);
reader.ReadInto(restriction.via);
reader.ReadInto(restriction.to);
}
restrictions.push_back(std::move(restriction));
num_indices--;
inline void write(storage::io::FileWriter &writer, const NodeRestriction &restriction)
{
writer.WriteOne(restriction.from);
writer.WriteOne(restriction.via);
writer.WriteOne(restriction.to);
}
inline void read(storage::io::FileReader &reader, WayRestriction &restriction)
{
read(reader, restriction.in_restriction);
read(reader, restriction.out_restriction);
}
inline void write(storage::io::FileWriter &writer, const WayRestriction &restriction)
{
write(writer, restriction.in_restriction);
write(writer, restriction.out_restriction);
}
inline void read(storage::io::FileReader &reader, TurnRestriction &restriction)
{
reader.ReadInto(restriction.flags);
if (restriction.Type() == RestrictionType::WAY_RESTRICTION)
{
WayRestriction way_restriction;
read(reader, way_restriction);
restriction.node_or_way = std::move(way_restriction);
}
else
{
BOOST_ASSERT(restriction.Type() == RestrictionType::NODE_RESTRICTION);
NodeRestriction node_restriction;
read(reader, node_restriction);
restriction.node_or_way = std::move(node_restriction);
}
}
inline void write(storage::io::FileWriter &writer, const TurnRestriction &restriction)
{
writer.WriteOne(restriction.via);
writer.WriteOne(restriction.from);
writer.WriteOne(restriction.to);
writer.WriteOne(restriction.flags.is_only);
writer.WriteOne(restriction.flags);
if (restriction.Type() == RestrictionType::WAY_RESTRICTION)
{
write(writer, boost::get<WayRestriction>(restriction.node_or_way));
}
else
{
BOOST_ASSERT(restriction.Type() == RestrictionType::NODE_RESTRICTION);
write(writer, boost::get<NodeRestriction>(restriction.node_or_way));
}
}
inline void write(storage::io::FileWriter &writer, const ConditionalTurnRestriction &restriction)
{
write(writer, static_cast<TurnRestriction>(restriction));
writer.WriteElementCount64(restriction.condition.size());
for (const auto &c : restriction.condition)
{
@ -181,6 +207,72 @@ inline void write(storage::io::FileWriter &writer, const TurnRestriction &restri
storage::serialization::write(writer, c.monthdays);
}
}
inline void read(storage::io::FileReader &reader, ConditionalTurnRestriction &restriction)
{
TurnRestriction base;
read(reader, base);
reinterpret_cast<TurnRestriction &>(restriction) = std::move(base);
auto num_conditions = reader.ReadElementCount64();
restriction.condition.resize(num_conditions);
for (uint64_t i = 0; i < num_conditions; i++)
{
reader.ReadInto(restriction.condition[i].modifier);
storage::serialization::read(reader, restriction.condition[i].times);
storage::serialization::read(reader, restriction.condition[i].weekdays);
storage::serialization::read(reader, restriction.condition[i].monthdays);
}
}
// read/write for conditional turn restrictions file
inline void read(storage::io::FileReader &reader, std::vector<TurnRestriction> &restrictions)
{
auto num_indices = reader.ReadElementCount64();
restrictions.reserve(num_indices);
TurnRestriction restriction;
while (num_indices > 0)
{
read(reader, restriction);
restrictions.push_back(std::move(restriction));
num_indices--;
}
}
inline void write(storage::io::FileWriter &writer, const std::vector<TurnRestriction> &restrictions)
{
std::uint64_t num_indices = restrictions.size();
writer.WriteElementCount64(num_indices);
auto const write_restriction = [&writer](const auto &restriction) {
write(writer, restriction);
};
std::for_each(restrictions.begin(), restrictions.end(), write_restriction);
}
// read/write for conditional turn restrictions file
inline void read(storage::io::FileReader &reader,
std::vector<ConditionalTurnRestriction> &restrictions)
{
auto num_indices = reader.ReadElementCount64();
restrictions.reserve(num_indices);
ConditionalTurnRestriction restriction;
while (num_indices > 0)
{
read(reader, restriction);
restrictions.push_back(std::move(restriction));
num_indices--;
}
}
inline void write(storage::io::FileWriter &writer,
const std::vector<ConditionalTurnRestriction> &restrictions)
{
std::uint64_t num_indices = restrictions.size();
writer.WriteElementCount64(num_indices);
auto const write_restriction = [&writer](const auto &restriction) {
write(writer, restriction);
};
std::for_each(restrictions.begin(), restrictions.end(), write_restriction);
}
}
}
}

View File

@ -52,12 +52,12 @@ using OSMWayID = osrm::Alias<std::uint64_t, tag::osm_way_id>;
static_assert(std::is_pod<OSMWayID>(), "OSMWayID is not a valid alias");
static const OSMNodeID SPECIAL_OSM_NODEID = OSMNodeID{std::numeric_limits<std::uint64_t>::max()};
static const OSMWayID SPECIAL_OSM_WAYID = OSMWayID{std::numeric_limits<std::uint32_t>::max()};
static const OSMWayID SPECIAL_OSM_WAYID = OSMWayID{std::numeric_limits<std::uint64_t>::max()};
static const OSMNodeID MAX_OSM_NODEID = OSMNodeID{std::numeric_limits<std::uint64_t>::max()};
static const OSMNodeID MIN_OSM_NODEID = OSMNodeID{std::numeric_limits<std::uint64_t>::min()};
static const OSMWayID MAX_OSM_WAYID = OSMWayID{std::numeric_limits<std::uint32_t>::max()};
static const OSMWayID MIN_OSM_WAYID = OSMWayID{std::numeric_limits<std::uint32_t>::min()};
static const OSMWayID MAX_OSM_WAYID = OSMWayID{std::numeric_limits<std::uint64_t>::max()};
static const OSMWayID MIN_OSM_WAYID = OSMWayID{std::numeric_limits<std::uint64_t>::min()};
using OSMNodeID_weak = std::uint64_t;
using OSMEdgeID_weak = std::uint64_t;

View File

@ -139,7 +139,7 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme
WriteEdges(file_out);
PrepareRestrictions();
WriteRestrictions(restrictions_file_name);
WriteConditionalRestrictions(restrictions_file_name);
WriteCharData(name_file_name);
}
@ -633,46 +633,19 @@ void ExtractionContainers::WriteNodes(storage::io::FileWriter &file_out) const
util::Log() << "Processed " << max_internal_node_id << " nodes";
}
void ExtractionContainers::WriteRestrictions(const std::string &path)
void ExtractionContainers::WriteConditionalRestrictions(const std::string &path)
{
// serialize restrictions
std::uint64_t written_restriction_count = 0;
std::uint64_t written_restriction_count = conditional_turn_restrictions.size();
storage::io::FileWriter restrictions_out_file(path,
storage::io::FileWriter::GenerateFingerprint);
restrictions_out_file.WriteElementCount64(written_restriction_count);
for (const auto &restriction_container : restrictions_list)
{
if (SPECIAL_NODEID != restriction_container.restriction.from.node &&
SPECIAL_NODEID != restriction_container.restriction.via.node &&
SPECIAL_NODEID != restriction_container.restriction.to.node)
{
if (!restriction_container.restriction.condition.empty())
{
// write conditional turn restrictions to disk, for use in contractor later
extractor::serialization::write(restrictions_out_file,
restriction_container.restriction);
++written_restriction_count;
}
else
{
// save unconditional turn restriction to memory, for use in ebg later
unconditional_turn_restrictions.push_back(
std::move(restriction_container.restriction));
}
}
}
restrictions_out_file.SkipToBeginning();
restrictions_out_file.WriteElementCount64(written_restriction_count);
util::Log() << "number of restrictions saved to memory: "
<< unconditional_turn_restrictions.size();
serialization::write(restrictions_out_file, conditional_turn_restrictions);
util::Log() << "number of conditional restrictions written to disk: "
<< written_restriction_count;
}
void ExtractionContainers::PrepareRestrictions()
{
{
util::UnbufferedLog log;
log << "Sorting used ways ... ";
@ -684,211 +657,211 @@ void ExtractionContainers::PrepareRestrictions()
log << "ok, after " << TIMER_SEC(sort_ways) << "s";
}
// contain the start/end nodes of each way that is part of an restriction
std::unordered_map<OSMWayID, FirstAndLastSegmentOfWay> referenced_ways;
// enter invalid IDs into the above maps to indicate that we want to find out about start/end
// nodes of these ways
const auto mark_ids = [&](auto const &turn_restriction) {
FirstAndLastSegmentOfWay dummy_segment{
MAX_OSM_WAYID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID};
if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION)
{
const auto &way = turn_restriction.AsWayRestriction();
referenced_ways[OSMWayID{way.from}] = dummy_segment;
referenced_ways[OSMWayID{way.to}] = dummy_segment;
referenced_ways[OSMWayID{way.via}] = dummy_segment;
}
else
{
BOOST_ASSERT(turn_restriction.Type() == RestrictionType::NODE_RESTRICTION);
const auto &node = turn_restriction.AsNodeRestriction();
referenced_ways[OSMWayID{node.from}] = dummy_segment;
referenced_ways[OSMWayID{node.to}] = dummy_segment;
}
};
// update the values for all edges already sporting SPECIAL_NODEID
const auto set_ids = [&](auto const &start_end) {
auto itr = referenced_ways.find(start_end.way_id);
if (itr != referenced_ways.end())
itr->second = start_end;
};
// prepare for extracting source/destination nodes for all restrictions
{
util::UnbufferedLog log;
log << "Sorting " << restrictions_list.size() << " restriction. by from... ";
TIMER_START(sort_restrictions);
tbb::parallel_sort(
restrictions_list.begin(), restrictions_list.end(), CmpRestrictionContainerByFrom());
TIMER_STOP(sort_restrictions);
log << "ok, after " << TIMER_SEC(sort_restrictions) << "s";
log << "Collecting start/end information on " << restrictions_list.size()
<< " restrictions...";
TIMER_START(prepare_restrictions);
std::for_each(restrictions_list.begin(), restrictions_list.end(), mark_ids);
std::for_each(way_start_end_id_list.cbegin(), way_start_end_id_list.cend(), set_ids);
TIMER_STOP(prepare_restrictions);
log << "ok, after " << TIMER_SEC(prepare_restrictions) << "s";
}
{
util::UnbufferedLog log;
log << "Fixing restriction starts ... " << std::flush;
TIMER_START(fix_restriction_starts);
auto restrictions_iterator = restrictions_list.begin();
auto way_start_and_end_iterator = way_start_end_id_list.cbegin();
const auto restrictions_list_end = restrictions_list.end();
const auto way_start_end_id_list_end = way_start_end_id_list.cend();
while (way_start_and_end_iterator != way_start_end_id_list_end &&
restrictions_iterator != restrictions_list_end)
auto const to_internal = [&](auto const osm_node) {
auto internal = mapExternalToInternalNodeID(
used_node_id_list.begin(), used_node_id_list.end(), OSMNodeID{osm_node});
if (internal == SPECIAL_NODEID)
{
if (way_start_and_end_iterator->way_id <
OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.from.way)})
util::Log(logDEBUG) << "Restriction references invalid node: " << osm_node;
}
return internal;
};
// Given:
// a -- b - ????????? - c -- d
// Given
// a -- b - ????????? - c -- d as via segment
// and either
// d -- e - ????????? - f -- g or
// h -- i - ????????? - j -- a
// (d,e) or (j,a) as entry-segment
auto const find_node_restriction =
[&](auto const &segment, auto const &via_segment, auto const via_node) {
// connected at the front of the segment
if (via_node == MAX_OSM_NODEID || segment.first_segment_source_id == via_node)
{
++way_start_and_end_iterator;
continue;
}
if (way_start_and_end_iterator->way_id >
OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.from.way)})
{
util::Log(logDEBUG) << "Restriction references invalid way: "
<< restrictions_iterator->restriction.from.way;
restrictions_iterator->restriction.from.node = SPECIAL_NODEID;
++restrictions_iterator;
continue;
}
BOOST_ASSERT(
way_start_and_end_iterator->way_id ==
OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.from.way)});
// we do not remap the via id yet, since we will need it for the to node as well
const OSMNodeID via_osm_node_id =
OSMNodeID{restrictions_iterator->restriction.via.node};
// check if via is actually valid, if not invalidate
auto via_node_id = mapExternalToInternalNodeID(
used_node_id_list.begin(), used_node_id_list.end(), via_osm_node_id);
if (via_node_id == SPECIAL_NODEID)
{
util::Log(logDEBUG) << "Restriction references invalid node: " << via_osm_node_id;
restrictions_iterator->restriction.via.node = SPECIAL_NODEID;
++restrictions_iterator;
continue;
}
if (way_start_and_end_iterator->first_segment_source_id == via_osm_node_id)
{
// assign new from node id
const auto from_node_id = mapExternalToInternalNodeID(
used_node_id_list.begin(),
used_node_id_list.end(),
way_start_and_end_iterator->first_segment_target_id);
if (from_node_id == SPECIAL_NODEID)
if (segment.first_segment_source_id == via_segment.first_segment_source_id)
{
util::Log(logDEBUG) << "Way references invalid node: "
<< way_start_and_end_iterator->first_segment_target_id;
restrictions_iterator->restriction.from.node = SPECIAL_NODEID;
++restrictions_iterator;
++way_start_and_end_iterator;
continue;
return NodeRestriction{to_internal(segment.first_segment_target_id),
to_internal(segment.first_segment_source_id),
to_internal(via_segment.first_segment_target_id)};
}
restrictions_iterator->restriction.from.node = from_node_id;
}
else if (way_start_and_end_iterator->last_segment_target_id == via_osm_node_id)
{
// assign new from node id
const auto from_node_id =
mapExternalToInternalNodeID(used_node_id_list.begin(),
used_node_id_list.end(),
way_start_and_end_iterator->last_segment_source_id);
if (from_node_id == SPECIAL_NODEID)
else if (segment.first_segment_source_id == via_segment.last_segment_target_id)
{
util::Log(logDEBUG) << "Way references invalid node: "
<< way_start_and_end_iterator->last_segment_target_id;
restrictions_iterator->restriction.from.node = SPECIAL_NODEID;
++restrictions_iterator;
++way_start_and_end_iterator;
continue;
return NodeRestriction{to_internal(segment.first_segment_target_id),
to_internal(segment.first_segment_source_id),
to_internal(via_segment.last_segment_source_id)};
}
restrictions_iterator->restriction.from.node = from_node_id;
}
else
// connected at the end of the segment
if (via_node == MAX_OSM_NODEID || segment.last_segment_target_id == via_node)
{
// if it's neither, this is an invalid restriction
restrictions_iterator->restriction.from.node = SPECIAL_NODEID;
if (segment.last_segment_target_id == via_segment.first_segment_source_id)
{
return NodeRestriction{to_internal(segment.last_segment_source_id),
to_internal(segment.last_segment_target_id),
to_internal(via_segment.first_segment_target_id)};
}
else if (segment.last_segment_target_id == via_segment.last_segment_target_id)
{
return NodeRestriction{to_internal(segment.last_segment_source_id),
to_internal(segment.last_segment_target_id),
to_internal(via_segment.last_segment_source_id)};
}
}
++restrictions_iterator;
// unconnected
util::Log(logDEBUG) << "Restriction references unconnected way: " << segment.way_id;
return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
};
// translate the turn from one segment onto another into a node restriction (the ways can only
// be connected at a single location)
auto const get_node_restriction_from_OSM_ids = [&](
auto const from_id, auto const to_id, const OSMNodeID via_node = MAX_OSM_NODEID) {
auto const from_segment_itr = referenced_ways.find(OSMWayID{from_id});
if (from_segment_itr->second.way_id != OSMWayID{from_id})
{
util::Log(logDEBUG) << "Restriction references invalid way: " << from_id;
return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
TIMER_STOP(fix_restriction_starts);
log << "ok, after " << TIMER_SEC(fix_restriction_starts) << "s";
}
{
util::UnbufferedLog log;
log << "Sorting restrictions. by to ... " << std::flush;
TIMER_START(sort_restrictions_to);
tbb::parallel_sort(
restrictions_list.begin(), restrictions_list.end(), CmpRestrictionContainerByTo());
TIMER_STOP(sort_restrictions_to);
log << "ok, after " << TIMER_SEC(sort_restrictions_to) << "s";
}
{
util::UnbufferedLog log;
log << "Fixing restriction ends ... " << std::flush;
TIMER_START(fix_restriction_ends);
auto restrictions_iterator = restrictions_list.begin();
auto way_start_and_end_iterator = way_start_end_id_list.cbegin();
const auto way_start_end_id_list_end_ = way_start_end_id_list.cend();
const auto restrictions_list_end_ = restrictions_list.end();
while (way_start_and_end_iterator != way_start_end_id_list_end_ &&
restrictions_iterator != restrictions_list_end_)
auto const to_segment_itr = referenced_ways.find(OSMWayID{to_id});
if (to_segment_itr->second.way_id != OSMWayID{to_id})
{
if (way_start_and_end_iterator->way_id <
OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.to.way)})
{
++way_start_and_end_iterator;
continue;
}
if (restrictions_iterator->restriction.from.node == SPECIAL_NODEID ||
restrictions_iterator->restriction.via.node == SPECIAL_NODEID)
{
++restrictions_iterator;
continue;
}
if (way_start_and_end_iterator->way_id >
OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.to.way)})
{
util::Log(logDEBUG) << "Restriction references invalid way: "
<< restrictions_iterator->restriction.to.way;
restrictions_iterator->restriction.to.way = SPECIAL_NODEID;
++restrictions_iterator;
continue;
}
BOOST_ASSERT(
way_start_and_end_iterator->way_id ==
OSMWayID{static_cast<std::uint32_t>(restrictions_iterator->restriction.to.way)});
const OSMNodeID via_osm_node_id =
OSMNodeID{restrictions_iterator->restriction.via.node};
// assign new via node id
const auto via_node_id = mapExternalToInternalNodeID(
used_node_id_list.begin(), used_node_id_list.end(), via_osm_node_id);
BOOST_ASSERT(via_node_id != SPECIAL_NODEID);
restrictions_iterator->restriction.via.node = via_node_id;
if (way_start_and_end_iterator->first_segment_source_id == via_osm_node_id)
{
const auto to_node_id = mapExternalToInternalNodeID(
used_node_id_list.begin(),
used_node_id_list.end(),
way_start_and_end_iterator->first_segment_target_id);
if (to_node_id == SPECIAL_NODEID)
{
util::Log(logDEBUG) << "Way references invalid node: "
<< way_start_and_end_iterator->first_segment_source_id;
restrictions_iterator->restriction.to.node = SPECIAL_NODEID;
++restrictions_iterator;
++way_start_and_end_iterator;
continue;
}
restrictions_iterator->restriction.to.node = to_node_id;
}
else if (way_start_and_end_iterator->last_segment_target_id == via_osm_node_id)
{
const auto to_node_id =
mapExternalToInternalNodeID(used_node_id_list.begin(),
used_node_id_list.end(),
way_start_and_end_iterator->last_segment_source_id);
if (to_node_id == SPECIAL_NODEID)
{
util::Log(logDEBUG) << "Way references invalid node: "
<< way_start_and_end_iterator->last_segment_source_id;
restrictions_iterator->restriction.to.node = SPECIAL_NODEID;
++restrictions_iterator;
++way_start_and_end_iterator;
continue;
}
restrictions_iterator->restriction.to.node = to_node_id;
}
else
{
// if it's neither, this is an invalid restriction
restrictions_iterator->restriction.to.node = SPECIAL_NODEID;
}
++restrictions_iterator;
util::Log(logDEBUG) << "Restriction references invalid way: " << to_id;
return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
TIMER_STOP(fix_restriction_ends);
log << "ok, after " << TIMER_SEC(fix_restriction_ends) << "s";
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 = [&](auto const &external_type, auto &internal_type) {
if (external_type.Type() == RestrictionType::WAY_RESTRICTION)
{
auto const &external = external_type.AsWayRestriction();
// check if we were able to resolve all the involved ways
auto const from_restriction =
get_node_restriction_from_OSM_ids(external.from, external.via);
auto const to_restriction =
get_node_restriction_from_OSM_ids(external.via, external.to);
// failed to translate either of the involved nodes?
if (!from_restriction.Valid() || !to_restriction.Valid())
return false;
// point located at both via and segment is alway on `second`, to FSSF is the order we
// need
WayRestriction way_restriction{from_restriction, to_restriction};
internal_type.node_or_way = std::move(way_restriction);
return true;
}
else
{
BOOST_ASSERT(external_type.Type() == RestrictionType::NODE_RESTRICTION);
auto const &external = external_type.AsNodeRestriction();
auto const via_node = to_internal(external.via);
// check if we were able to resolve all the involved ways
auto restriction = get_node_restriction_from_OSM_ids(
external.from, external.to, OSMNodeID{external.via});
if (!restriction.Valid())
{
std::cout << " >>> Invalid" << std::endl;
return false;
}
if (restriction.via != via_node)
{
util::Log(logDEBUG) << "Restriction references invalid way: " << external.via;
return false;
}
internal_type.node_or_way = std::move(restriction);
return true;
}
};
// wrapper function to handle distinction between conditional and unconditional turn
// restrictions
const auto transform_into_internal_types = [&](auto &external_restriction) {
// unconditional restriction
if (external_restriction.condition.empty())
{
TurnRestriction restriction;
restriction.flags = external_restriction.flags;
if (transform(external_restriction, restriction))
unconditional_turn_restrictions.push_back(restriction);
}
// conditional turn restriction
else
{
ConditionalTurnRestriction restriction;
restriction.flags = external_restriction.flags;
restriction.condition = std::move(external_restriction.condition);
if (transform(external_restriction, restriction))
conditional_turn_restrictions.push_back(restriction);
}
};
// Transforming the restrictions into the dedicated internal types
{
util::UnbufferedLog log;
log << "Collecting start/end information on " << restrictions_list.size()
<< " restrictions...";
TIMER_START(transform);
std::for_each(
restrictions_list.begin(), restrictions_list.end(), transform_into_internal_types);
TIMER_STOP(transform);
log << "ok, after " << TIMER_SEC(transform) << "s";
}
}
}
}
} // namespace extractor
} // namespace osrm

View File

@ -259,7 +259,7 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment,
SharedBuffer buffer;
std::vector<std::pair<const osmium::Node &, ExtractionNode>> resulting_nodes;
std::vector<std::pair<const osmium::Way &, ExtractionWay>> resulting_ways;
std::vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
std::vector<InputConditionalTurnRestriction> resulting_restrictions;
};
tbb::filter_t<void, SharedBuffer> buffer_reader(
@ -454,7 +454,6 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
std::unordered_set<NodeID> barrier_nodes;
std::unordered_set<NodeID> traffic_lights;
auto restriction_map = std::make_shared<RestrictionMap>(turn_restrictions);
auto node_based_graph =
LoadNodeBasedGraph(barrier_nodes, traffic_lights, coordinates, osm_node_ids);
@ -462,12 +461,13 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
GraphCompressor graph_compressor;
graph_compressor.Compress(barrier_nodes,
traffic_lights,
*restriction_map,
turn_restrictions,
*node_based_graph,
compressed_edge_container);
util::NameTable name_table(config.GetPath(".osrm.names").string());
auto restriction_map = std::make_shared<RestrictionMap>(turn_restrictions);
EdgeBasedGraphFactory edge_based_graph_factory(
node_based_graph,
compressed_edge_container,

View File

@ -73,19 +73,12 @@ void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node,
}
}
void ExtractorCallbacks::ProcessRestriction(
const boost::optional<InputRestrictionContainer> &restriction)
void ExtractorCallbacks::ProcessRestriction(const InputConditionalTurnRestriction &restriction)
{
if (restriction)
{
external_memory.restrictions_list.push_back(restriction.get());
// util::Log() << "from: " << restriction.get().restriction.from.node <<
// ",via: " << restriction.get().restriction.via.node <<
// ", to: " << restriction.get().restriction.to.node <<
// ", only: " << (restriction.get().restriction.flags.is_only ?
// "y" : "n");
}
external_memory.restrictions_list.push_back(restriction);
// util::Log() << restriction.toString();
}
/**
* Takes the geometry contained in the ```input_way``` and the tags computed
* by the lua profile inside ```parsed_way``` and computes all edge segments.

View File

@ -1,13 +1,18 @@
#include "extractor/graph_compressor.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "extractor/restriction_map.hpp"
#include "extractor/restriction.hpp"
#include "extractor/restriction_compressor.hpp"
#include "util/dynamic_graph.hpp"
#include "util/node_based_graph.hpp"
#include "util/percent.hpp"
#include "util/log.hpp"
#include <boost/assert.hpp>
#include <unordered_set>
namespace osrm
{
namespace extractor
@ -15,13 +20,35 @@ namespace extractor
void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
const std::unordered_set<NodeID> &traffic_lights,
RestrictionMap &restriction_map,
std::vector<TurnRestriction> &turn_restrictions,
util::NodeBasedDynamicGraph &graph,
CompressedEdgeContainer &geometry_compressor)
{
const unsigned original_number_of_nodes = graph.GetNumberOfNodes();
const unsigned original_number_of_edges = graph.GetNumberOfEdges();
RestrictionCompressor restriction_compressor(turn_restrictions);
// we do not compress turn restrictions on degree two nodes. These nodes are usually used to
// indicated `directed` barriers
std::unordered_set<NodeID> restriction_via_nodes;
const auto remember_via_nodes = [&](const auto &restriction) {
if (restriction.Type() == RestrictionType::NODE_RESTRICTION)
{
const auto &node = restriction.AsNodeRestriction();
restriction_via_nodes.insert(node.via);
}
else
{
BOOST_ASSERT(restriction.Type() == RestrictionType::WAY_RESTRICTION);
const auto &way = restriction.AsWayRestriction();
restriction_via_nodes.insert(way.in_restriction.via);
restriction_via_nodes.insert(way.out_restriction.via);
}
};
std::for_each(turn_restrictions.begin(), turn_restrictions.end(), remember_via_nodes);
{
util::UnbufferedLog log;
util::Percent progress(log, original_number_of_nodes);
@ -43,7 +70,7 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
}
// check if v is a via node for a turn restriction, i.e. a 'directed' barrier node
if (restriction_map.IsViaNode(node_v))
if (restriction_via_nodes.count(node_v))
{
continue;
}
@ -199,11 +226,7 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
graph.DeleteEdge(node_v, reverse_e2);
// update any involved turn restrictions
restriction_map.FixupStartingTurnRestriction(node_u, node_v, node_w);
restriction_map.FixupArrivingTurnRestriction(node_u, node_v, node_w, graph);
restriction_map.FixupStartingTurnRestriction(node_w, node_v, node_u);
restriction_map.FixupArrivingTurnRestriction(node_w, node_v, node_u, graph);
restriction_compressor.Compress(node_u, node_v, node_w);
// store compressed geometry in container
geometry_compressor.CompressEdge(forward_e1,

View File

@ -0,0 +1,104 @@
#include "extractor/restriction_compressor.hpp"
#include "extractor/restriction.hpp"
#include <algorithm>
#include <boost/assert.hpp>
#include <utility>
namespace osrm
{
namespace extractor
{
RestrictionCompressor::RestrictionCompressor(std::vector<TurnRestriction> &restrictions)
{
// add a node restriction ptr to the heads/tails maps, needs to be a reference!
auto index = [&](auto &element) {
heads.insert(std::make_pair(element.from, &element));
tails.insert(std::make_pair(element.to, &element));
};
// !needs to be reference, so we can get the correct address
const auto index_heads_and_tails = [&](auto &restriction) {
if (restriction.Type() == RestrictionType::WAY_RESTRICTION)
{
auto &way_restriction = restriction.AsWayRestriction();
index(way_restriction.in_restriction);
index(way_restriction.out_restriction);
}
else
{
BOOST_ASSERT(restriction.Type() == RestrictionType::NODE_RESTRICTION);
auto &node_restriction = restriction.AsNodeRestriction();
index(node_restriction);
}
};
// add all restrictions as their respective head-tail pointers
std::for_each(restrictions.begin(), restrictions.end(), index_heads_and_tails);
}
void RestrictionCompressor::Compress(const NodeID from, const NodeID via, const NodeID to)
{
const auto get_value = [](const auto pair) { return pair.second; };
// extract all head ptrs and move them from via to from.
auto all_heads_range = heads.equal_range(via);
std::vector<NodeRestriction *> head_ptrs;
std::transform(
all_heads_range.first, all_heads_range.second, std::back_inserter(head_ptrs), get_value);
const auto update_head = [&](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(head_ptrs.begin(), head_ptrs.end(), update_head);
const auto reinsert_head = [&](auto ptr) { heads.insert(std::make_pair(ptr->from, ptr)); };
// update the ptrs in our mapping
heads.erase(via);
std::for_each(head_ptrs.begin(), head_ptrs.end(), reinsert_head);
// extract all tail ptrs and move them from via to to
auto all_tails_range = tails.equal_range(via);
std::vector<NodeRestriction *> tail_ptrs;
std::transform(
all_tails_range.first, all_tails_range.second, std::back_inserter(tail_ptrs), get_value);
const auto update_tail = [&](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;
}
};
const auto reinsert_tail = [&](auto ptr) { tails.insert(std::make_pair(ptr->to, ptr)); };
std::for_each(tail_ptrs.begin(), tail_ptrs.end(), update_tail);
// update tail ptrs in mapping
tails.erase(via);
std::for_each(tail_ptrs.begin(), tail_ptrs.end(), reinsert_tail);
}
} // namespace extractor
} // namespace osrm

View File

@ -1,5 +1,7 @@
#include "extractor/restriction_map.hpp"
#include <boost/assert.hpp>
namespace osrm
{
namespace extractor
@ -11,17 +13,23 @@ RestrictionMap::RestrictionMap(const std::vector<TurnRestriction> &restriction_l
// a pair of starting edge and a list of all end nodes
for (auto &restriction : restriction_list)
{
// only handle node restrictions here
if (restriction.Type() == RestrictionType::WAY_RESTRICTION)
continue;
const auto &node_restriction = restriction.AsNodeRestriction();
BOOST_ASSERT(node_restriction.Valid());
// This downcasting is OK because when this is called, the node IDs have been
// renumbered into internal values, which should be well under 2^32
// This will be a problem if we have more than 2^32 actual restrictions
BOOST_ASSERT(restriction.from.node < std::numeric_limits<NodeID>::max());
BOOST_ASSERT(restriction.via.node < std::numeric_limits<NodeID>::max());
m_restriction_start_nodes.insert(restriction.from.node);
m_no_turn_via_node_set.insert(restriction.via.node);
BOOST_ASSERT(node_restriction.from < std::numeric_limits<NodeID>::max());
BOOST_ASSERT(node_restriction.via < std::numeric_limits<NodeID>::max());
m_restriction_start_nodes.insert(node_restriction.from);
m_no_turn_via_node_set.insert(node_restriction.via);
// This explicit downcasting is also OK for the same reason.
RestrictionSource restriction_source = {static_cast<NodeID>(restriction.from.node),
static_cast<NodeID>(restriction.via.node)};
RestrictionSource restriction_source = {static_cast<NodeID>(node_restriction.from),
static_cast<NodeID>(node_restriction.via)};
std::size_t index;
auto restriction_iter = m_restriction_map.find(restriction_source);
@ -47,8 +55,7 @@ RestrictionMap::RestrictionMap(const std::vector<TurnRestriction> &restriction_l
}
}
++m_count;
BOOST_ASSERT(restriction.to.node < std::numeric_limits<NodeID>::max());
m_restriction_bucket_list.at(index).emplace_back(restriction.to.node,
m_restriction_bucket_list.at(index).emplace_back(node_restriction.to,
restriction.flags.is_only);
}
}

View File

@ -54,14 +54,13 @@ RestrictionParser::RestrictionParser(bool use_turn_restrictions_,
* in the corresponding profile. We use it for both namespacing restrictions, as in
* restriction:motorcar as well as whitelisting if its in except:motorcar.
*/
std::vector<InputRestrictionContainer>
boost::optional<InputConditionalTurnRestriction>
RestrictionParser::TryParse(const osmium::Relation &relation) const
{
std::vector<InputRestrictionContainer> parsed_restrictions;
// return if turn restrictions should be ignored
if (!use_turn_restrictions)
{
return {};
return boost::none;
}
osmium::tags::KeyFilter filter(false);
@ -90,14 +89,14 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
// if it's not a restriction, continue;
if (std::distance(fi_begin, fi_end) == 0)
{
return {};
return boost::none;
}
// check if the restriction should be ignored
const char *except = relation.get_value_by_key("except");
if (except != nullptr && ShouldIgnoreRestriction(except))
{
return {};
return boost::none;
}
bool is_only_restriction = false;
@ -119,11 +118,17 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
}
else // unrecognized value type
{
return {};
return boost::none;
}
}
InputRestrictionContainer restriction_container(is_only_restriction);
// we pretend every restriction is a conditional restriction. If we do not find any restriction,
// we can trim away the vector after parsing
InputConditionalTurnRestriction restriction_container;
restriction_container.flags.is_only = is_only_restriction;
boost::optional<std::uint64_t> from = boost::none, via = boost::none, to = boost::none;
bool is_node_restriction;
for (const auto &member : relation.members())
{
@ -136,33 +141,35 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
switch (member.type())
{
case osmium::item_type::node:
{
// Make sure nodes appear only in the role if a via node
if (0 == strcmp("from", role) || 0 == strcmp("to", role))
{
continue;
}
BOOST_ASSERT(0 == strcmp("via", role));
via = static_cast<std::uint64_t>(member.ref());
is_node_restriction = true;
// set via node id
restriction_container.restriction.via.node = member.ref();
break;
}
case osmium::item_type::way:
BOOST_ASSERT(0 == strcmp("from", role) || 0 == strcmp("to", role) ||
0 == strcmp("via", role));
if (0 == strcmp("from", role))
{
restriction_container.restriction.from.way = member.ref();
from = static_cast<std::uint64_t>(member.ref());
}
else if (0 == strcmp("to", role))
{
restriction_container.restriction.to.way = member.ref();
to = static_cast<std::uint64_t>(member.ref());
}
else if (0 == strcmp("via", role))
{
via = static_cast<std::uint64_t>(member.ref());
is_node_restriction = false;
}
// else if (0 == strcmp("via", role))
// {
// not yet suppported
// restriction_container.restriction.via.way = member.ref();
// }
break;
case osmium::item_type::relation:
// not yet supported, but who knows what the future holds...
@ -194,20 +201,29 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
std::vector<util::OpeningHours> hours = util::ParseOpeningHours(p.condition);
// found unrecognized condition, continue
if (hours.empty())
return {};
return boost::none;
restriction_container.restriction.condition = std::move(hours);
restriction_container.condition = std::move(hours);
}
}
}
// push back a copy of turn restriction
if (restriction_container.restriction.via.node != SPECIAL_NODEID &&
restriction_container.restriction.from.node != SPECIAL_NODEID &&
restriction_container.restriction.to.node != SPECIAL_NODEID)
parsed_restrictions.push_back(restriction_container);
return parsed_restrictions;
if (from && via && to)
{
if (is_node_restriction)
{
restriction_container.node_or_way = InputNodeRestriction{*from, *via, *to};
}
else
{
restriction_container.node_or_way = InputWayRestriction{*from, *via, *to};
}
return restriction_container;
}
else
{
return boost::none;
}
}
bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_string) const

View File

@ -613,11 +613,10 @@ void Sol2ScriptingEnvironment::ProcessElements(
const RestrictionParser &restriction_parser,
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions)
std::vector<InputConditionalTurnRestriction> &resulting_restrictions)
{
ExtractionNode result_node;
ExtractionWay result_way;
std::vector<InputRestrictionContainer> result_res;
auto &local_context = this->GetSol2Context();
for (auto entity = buffer.cbegin(), end = buffer.cend(); entity != end; ++entity)
@ -645,14 +644,15 @@ void Sol2ScriptingEnvironment::ProcessElements(
static_cast<const osmium::Way &>(*entity), std::move(result_way)));
break;
case osmium::item_type::relation:
result_res.clear();
result_res =
{
auto result_res =
restriction_parser.TryParse(static_cast<const osmium::Relation &>(*entity));
for (const InputRestrictionContainer &r : result_res)
if (result_res)
{
resulting_restrictions.push_back(r);
resulting_restrictions.push_back(*result_res);
}
break;
}
break;
default:
break;
}

View File

@ -444,22 +444,18 @@ updateTurnPenalties(const UpdaterConfig &config,
}
bool IsRestrictionValid(const Timezoner &tz_handler,
const extractor::TurnRestriction &turn,
const std::vector<util::Coordinate> &coordinates,
const extractor::PackedOSMIDs &osm_node_ids)
const extractor::ConditionalTurnRestriction &turn,
const std::vector<util::Coordinate> &coordinates)
{
const auto via_node = osm_node_ids[turn.via.node];
const auto from_node = osm_node_ids[turn.from.node];
const auto to_node = osm_node_ids[turn.to.node];
if (turn.condition.empty())
{
osrm::util::Log(logWARNING) << "Condition parsing failed for the turn " << from_node
<< " -> " << via_node << " -> " << to_node;
return false;
}
BOOST_ASSERT(!turn.condition.empty());
const auto lon = static_cast<double>(toFloating(coordinates[turn.via.node].lon));
const auto lat = static_cast<double>(toFloating(coordinates[turn.via.node].lat));
// we utilize the via node (first on ways) to represent the turn restriction
auto const via = turn.Type() == extractor::RestrictionType::WAY_RESTRICTION
? turn.AsWayRestriction().in_restriction.to
: turn.AsNodeRestriction().via;
const auto lon = static_cast<double>(toFloating(coordinates[via].lon));
const auto lat = static_cast<double>(toFloating(coordinates[via].lat));
const auto &condition = turn.condition;
// Get local time of the restriction
@ -482,9 +478,8 @@ bool IsRestrictionValid(const Timezoner &tz_handler,
std::vector<std::uint64_t>
updateConditionalTurns(const UpdaterConfig &config,
std::vector<TurnPenalty> &turn_weight_penalties,
const std::vector<extractor::TurnRestriction> &conditional_turns,
const std::vector<extractor::ConditionalTurnRestriction> &conditional_turns,
std::vector<util::Coordinate> &coordinates,
extractor::PackedOSMIDs &osm_node_ids,
Timezoner time_zone_handler)
{
// Mapped file pointer for turn indices
@ -501,18 +496,28 @@ updateConditionalTurns(const UpdaterConfig &config,
std::unordered_set<std::tuple<NodeID, NodeID, NodeID>,
std::hash<std::tuple<NodeID, NodeID, NodeID>>>
is_no_set;
for (const auto &c : conditional_turns)
for (const auto &node_or_way : conditional_turns)
{
// only add restrictions to the lookups if the restriction is valid now
if (!IsRestrictionValid(time_zone_handler, c, coordinates, osm_node_ids))
// TODO handle conditional turn restrictions for via-ways (look-up doesn't work here)
// https://github.com/Project-OSRM/osrm-backend/issues/2681#issuecomment-313376385
if (node_or_way.Type() == extractor::RestrictionType::WAY_RESTRICTION)
continue;
if (c.flags.is_only)
if (!IsRestrictionValid(time_zone_handler, node_or_way, coordinates))
continue;
// TODO get rid of this, when we can handle way restrictions
const auto &c = node_or_way.AsNodeRestriction();
// only add restrictions to the lookups if the restriction is valid now
if (node_or_way.flags.is_only)
{
is_only_lookup.lookup.push_back({std::make_tuple(c.from.node, c.via.node), c.to.node});
is_only_lookup.lookup.push_back({std::make_tuple(c.from, c.via), c.to});
}
else
{
is_no_set.insert({std::make_tuple(c.from.node, c.via.node, c.to.node)});
is_no_set.insert({std::make_tuple(c.from, c.via, c.to)});
}
}
@ -633,7 +638,7 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
load_profile_properties);
}
std::vector<extractor::TurnRestriction> conditional_turns;
std::vector<extractor::ConditionalTurnRestriction> conditional_turns;
if (update_conditional_turns)
{
using storage::io::FileReader;
@ -691,12 +696,8 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
}
const Timezoner time_zone_handler = Timezoner(config.tz_file_path, config.valid_now);
auto updated_turn_penalties = updateConditionalTurns(config,
turn_weight_penalties,
conditional_turns,
coordinates,
osm_node_ids,
time_zone_handler);
auto updated_turn_penalties = updateConditionalTurns(
config, turn_weight_penalties, conditional_turns, coordinates, time_zone_handler);
const auto offset = updated_segments.size();
updated_segments.resize(offset + updated_turn_penalties.size());
// we need to re-compute all edges that have updated turn penalties.

View File

@ -1,6 +1,6 @@
#include "extractor/graph_compressor.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "extractor/restriction_map.hpp"
#include "extractor/restriction.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
@ -8,6 +8,8 @@
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <unordered_set>
#include <vector>
BOOST_AUTO_TEST_SUITE(graph_compressor)
@ -51,7 +53,7 @@ BOOST_AUTO_TEST_CASE(long_road_test)
std::unordered_set<NodeID> barrier_nodes;
std::unordered_set<NodeID> traffic_lights;
RestrictionMap map;
std::vector<TurnRestriction> restrictions;
CompressedEdgeContainer container;
std::vector<InputEdge> edges = {MakeUnitEdge(0, 1),
@ -68,7 +70,7 @@ BOOST_AUTO_TEST_CASE(long_road_test)
BOOST_ASSERT(edges[4].data.IsCompatibleTo(edges[6].data));
Graph graph(5, edges);
compressor.Compress(barrier_nodes, traffic_lights, map, graph, container);
compressor.Compress(barrier_nodes, traffic_lights, restrictions, graph, container);
BOOST_CHECK_EQUAL(graph.FindEdge(0, 1), SPECIAL_EDGEID);
BOOST_CHECK_EQUAL(graph.FindEdge(1, 2), SPECIAL_EDGEID);
@ -88,7 +90,7 @@ BOOST_AUTO_TEST_CASE(loop_test)
std::unordered_set<NodeID> barrier_nodes;
std::unordered_set<NodeID> traffic_lights;
RestrictionMap map;
std::vector<TurnRestriction> restrictions;
CompressedEdgeContainer container;
std::vector<InputEdge> edges = {MakeUnitEdge(0, 1),
@ -118,7 +120,7 @@ BOOST_AUTO_TEST_CASE(loop_test)
BOOST_ASSERT(edges[10].data.IsCompatibleTo(edges[11].data));
Graph graph(6, edges);
compressor.Compress(barrier_nodes, traffic_lights, map, graph, container);
compressor.Compress(barrier_nodes, traffic_lights, restrictions, graph, container);
BOOST_CHECK_EQUAL(graph.FindEdge(5, 0), SPECIAL_EDGEID);
BOOST_CHECK_EQUAL(graph.FindEdge(0, 1), SPECIAL_EDGEID);
@ -140,7 +142,7 @@ BOOST_AUTO_TEST_CASE(t_intersection)
std::unordered_set<NodeID> barrier_nodes;
std::unordered_set<NodeID> traffic_lights;
RestrictionMap map;
std::vector<TurnRestriction> restrictions;
CompressedEdgeContainer container;
std::vector<InputEdge> edges = {MakeUnitEdge(0, 1),
@ -157,7 +159,7 @@ BOOST_AUTO_TEST_CASE(t_intersection)
BOOST_ASSERT(edges[4].data.IsCompatibleTo(edges[5].data));
Graph graph(4, edges);
compressor.Compress(barrier_nodes, traffic_lights, map, graph, container);
compressor.Compress(barrier_nodes, traffic_lights, restrictions, graph, container);
BOOST_CHECK(graph.FindEdge(0, 1) != SPECIAL_EDGEID);
BOOST_CHECK(graph.FindEdge(1, 2) != SPECIAL_EDGEID);
@ -173,7 +175,7 @@ BOOST_AUTO_TEST_CASE(street_name_changes)
std::unordered_set<NodeID> barrier_nodes;
std::unordered_set<NodeID> traffic_lights;
RestrictionMap map;
std::vector<TurnRestriction> restrictions;
CompressedEdgeContainer container;
std::vector<InputEdge> edges = {
@ -184,7 +186,7 @@ BOOST_AUTO_TEST_CASE(street_name_changes)
BOOST_ASSERT(edges[2].data.IsCompatibleTo(edges[3].data));
Graph graph(5, edges);
compressor.Compress(barrier_nodes, traffic_lights, map, graph, container);
compressor.Compress(barrier_nodes, traffic_lights, restrictions, graph, container);
BOOST_CHECK(graph.FindEdge(0, 1) != SPECIAL_EDGEID);
BOOST_CHECK(graph.FindEdge(1, 2) != SPECIAL_EDGEID);
@ -199,7 +201,7 @@ BOOST_AUTO_TEST_CASE(direction_changes)
std::unordered_set<NodeID> barrier_nodes;
std::unordered_set<NodeID> traffic_lights;
RestrictionMap map;
std::vector<TurnRestriction> restrictions;
CompressedEdgeContainer container;
std::vector<InputEdge> edges = {
@ -208,7 +210,7 @@ BOOST_AUTO_TEST_CASE(direction_changes)
edges[1].data.reversed = true;
Graph graph(5, edges);
compressor.Compress(barrier_nodes, traffic_lights, map, graph, container);
compressor.Compress(barrier_nodes, traffic_lights, restrictions, graph, container);
BOOST_CHECK(graph.FindEdge(0, 1) != SPECIAL_EDGEID);
BOOST_CHECK(graph.FindEdge(1, 2) != SPECIAL_EDGEID);