2016-01-02 11:13:44 -05:00
|
|
|
#include "extractor/graph_compressor.hpp"
|
2015-06-23 19:55:30 -04:00
|
|
|
|
2016-01-02 11:13:44 -05:00
|
|
|
#include "extractor/compressed_edge_container.hpp"
|
2017-07-20 08:03:39 -04:00
|
|
|
#include "extractor/extraction_turn.hpp"
|
2017-07-06 11:09:24 -04:00
|
|
|
#include "extractor/restriction.hpp"
|
|
|
|
#include "extractor/restriction_compressor.hpp"
|
2018-01-05 07:05:53 -05:00
|
|
|
#include "guidance/intersection.hpp"
|
2017-07-06 11:09:24 -04:00
|
|
|
|
2016-01-02 11:13:44 -05:00
|
|
|
#include "util/dynamic_graph.hpp"
|
|
|
|
#include "util/node_based_graph.hpp"
|
|
|
|
#include "util/percent.hpp"
|
2015-06-23 19:55:30 -04:00
|
|
|
|
2016-12-06 15:30:46 -05:00
|
|
|
#include "util/log.hpp"
|
2015-06-28 18:42:22 -04:00
|
|
|
|
2017-07-06 11:09:24 -04:00
|
|
|
#include <boost/assert.hpp>
|
|
|
|
#include <unordered_set>
|
|
|
|
|
2016-01-05 10:51:13 -05:00
|
|
|
namespace osrm
|
|
|
|
{
|
|
|
|
namespace extractor
|
|
|
|
{
|
|
|
|
|
2017-08-01 11:18:12 -04:00
|
|
|
void GraphCompressor::Compress(
|
|
|
|
const std::unordered_set<NodeID> &barrier_nodes,
|
|
|
|
const std::unordered_set<NodeID> &traffic_signals,
|
|
|
|
ScriptingEnvironment &scripting_environment,
|
|
|
|
std::vector<TurnRestriction> &turn_restrictions,
|
|
|
|
std::vector<ConditionalTurnRestriction> &conditional_turn_restrictions,
|
|
|
|
util::NodeBasedDynamicGraph &graph,
|
2017-09-25 09:37:11 -04:00
|
|
|
const std::vector<NodeBasedEdgeAnnotation> &node_data_container,
|
2017-08-01 11:18:12 -04:00
|
|
|
CompressedEdgeContainer &geometry_compressor)
|
2015-06-23 19:55:30 -04:00
|
|
|
{
|
|
|
|
const unsigned original_number_of_nodes = graph.GetNumberOfNodes();
|
|
|
|
const unsigned original_number_of_edges = graph.GetNumberOfEdges();
|
|
|
|
|
2017-08-01 11:18:12 -04:00
|
|
|
RestrictionCompressor restriction_compressor(turn_restrictions, conditional_turn_restrictions);
|
2017-07-06 11:09:24 -04:00
|
|
|
|
|
|
|
// 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);
|
2017-08-01 11:18:12 -04:00
|
|
|
std::for_each(conditional_turn_restrictions.begin(),
|
|
|
|
conditional_turn_restrictions.end(),
|
|
|
|
remember_via_nodes);
|
2017-07-06 11:09:24 -04:00
|
|
|
|
2015-06-23 19:55:30 -04:00
|
|
|
{
|
2017-07-20 08:03:39 -04:00
|
|
|
const auto weight_multiplier =
|
|
|
|
scripting_environment.GetProfileProperties().GetWeightMultiplier();
|
2016-12-06 15:30:46 -05:00
|
|
|
util::UnbufferedLog log;
|
|
|
|
util::Percent progress(log, original_number_of_nodes);
|
2015-06-23 19:55:30 -04:00
|
|
|
|
2016-12-06 15:30:46 -05:00
|
|
|
for (const NodeID node_v : util::irange(0u, original_number_of_nodes))
|
2015-06-23 19:55:30 -04:00
|
|
|
{
|
2016-12-06 15:30:46 -05:00
|
|
|
progress.PrintStatus(node_v);
|
2015-06-23 19:55:30 -04:00
|
|
|
|
2016-12-06 15:30:46 -05:00
|
|
|
// only contract degree 2 vertices
|
|
|
|
if (2 != graph.GetOutDegree(node_v))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2015-06-23 19:55:30 -04:00
|
|
|
|
2016-12-06 15:30:46 -05:00
|
|
|
// don't contract barrier node
|
|
|
|
if (barrier_nodes.end() != barrier_nodes.find(node_v))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2015-06-23 19:55:30 -04:00
|
|
|
|
2016-12-06 15:30:46 -05:00
|
|
|
// check if v is a via node for a turn restriction, i.e. a 'directed' barrier node
|
2017-07-06 11:09:24 -04:00
|
|
|
if (restriction_via_nodes.count(node_v))
|
2016-12-06 15:30:46 -05:00
|
|
|
{
|
2016-01-26 19:44:28 -05:00
|
|
|
continue;
|
2016-12-06 15:30:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// reverse_e2 forward_e2
|
|
|
|
// u <---------- v -----------> w
|
|
|
|
// ----------> <-----------
|
|
|
|
// forward_e1 reverse_e1
|
|
|
|
//
|
|
|
|
// Will be compressed to:
|
|
|
|
//
|
|
|
|
// reverse_e1
|
|
|
|
// u <---------- w
|
|
|
|
// ---------->
|
|
|
|
// forward_e1
|
|
|
|
//
|
|
|
|
// If the edges are compatible.
|
|
|
|
const bool reverse_edge_order = graph.GetEdgeData(graph.BeginEdges(node_v)).reversed;
|
|
|
|
const EdgeID forward_e2 = graph.BeginEdges(node_v) + reverse_edge_order;
|
|
|
|
BOOST_ASSERT(SPECIAL_EDGEID != forward_e2);
|
|
|
|
BOOST_ASSERT(forward_e2 >= graph.BeginEdges(node_v) &&
|
|
|
|
forward_e2 < graph.EndEdges(node_v));
|
|
|
|
const EdgeID reverse_e2 = graph.BeginEdges(node_v) + 1 - reverse_edge_order;
|
2017-09-25 09:37:11 -04:00
|
|
|
|
2016-12-06 15:30:46 -05:00
|
|
|
BOOST_ASSERT(SPECIAL_EDGEID != reverse_e2);
|
|
|
|
BOOST_ASSERT(reverse_e2 >= graph.BeginEdges(node_v) &&
|
|
|
|
reverse_e2 < graph.EndEdges(node_v));
|
|
|
|
|
|
|
|
const EdgeData &fwd_edge_data2 = graph.GetEdgeData(forward_e2);
|
|
|
|
const EdgeData &rev_edge_data2 = graph.GetEdgeData(reverse_e2);
|
|
|
|
|
|
|
|
const NodeID node_w = graph.GetTarget(forward_e2);
|
|
|
|
BOOST_ASSERT(SPECIAL_NODEID != node_w);
|
|
|
|
BOOST_ASSERT(node_v != node_w);
|
|
|
|
const NodeID node_u = graph.GetTarget(reverse_e2);
|
|
|
|
BOOST_ASSERT(SPECIAL_NODEID != node_u);
|
|
|
|
BOOST_ASSERT(node_u != node_v);
|
|
|
|
|
|
|
|
const EdgeID forward_e1 = graph.FindEdge(node_u, node_v);
|
|
|
|
BOOST_ASSERT(SPECIAL_EDGEID != forward_e1);
|
|
|
|
BOOST_ASSERT(node_v == graph.GetTarget(forward_e1));
|
|
|
|
const EdgeID reverse_e1 = graph.FindEdge(node_w, node_v);
|
|
|
|
BOOST_ASSERT(SPECIAL_EDGEID != reverse_e1);
|
|
|
|
BOOST_ASSERT(node_v == graph.GetTarget(reverse_e1));
|
|
|
|
|
|
|
|
const EdgeData &fwd_edge_data1 = graph.GetEdgeData(forward_e1);
|
|
|
|
const EdgeData &rev_edge_data1 = graph.GetEdgeData(reverse_e1);
|
2017-09-25 09:37:11 -04:00
|
|
|
const auto fwd_annotation_data1 = node_data_container[fwd_edge_data1.annotation_data];
|
|
|
|
const auto fwd_annotation_data2 = node_data_container[fwd_edge_data2.annotation_data];
|
|
|
|
const auto rev_annotation_data1 = node_data_container[rev_edge_data1.annotation_data];
|
|
|
|
const auto rev_annotation_data2 = node_data_container[rev_edge_data2.annotation_data];
|
2016-12-06 15:30:46 -05:00
|
|
|
|
|
|
|
if (graph.FindEdgeInEitherDirection(node_u, node_w) != SPECIAL_EDGEID)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2016-01-26 19:44:28 -05:00
|
|
|
|
2016-12-06 15:30:46 -05:00
|
|
|
// this case can happen if two ways with different names overlap
|
2017-09-25 09:37:11 -04:00
|
|
|
if ((fwd_annotation_data1.name_id != rev_annotation_data1.name_id) ||
|
|
|
|
(fwd_annotation_data2.name_id != rev_annotation_data2.name_id))
|
2016-12-06 15:30:46 -05:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-09-25 09:37:11 -04:00
|
|
|
if ((fwd_edge_data1.flags == fwd_edge_data2.flags) &&
|
|
|
|
(rev_edge_data1.flags == rev_edge_data2.flags) &&
|
|
|
|
(fwd_edge_data1.reversed == fwd_edge_data2.reversed) &&
|
|
|
|
(rev_edge_data1.reversed == rev_edge_data2.reversed) &&
|
|
|
|
// annotations need to match, except for the lane-id which can differ
|
|
|
|
fwd_annotation_data1.CanCombineWith(fwd_annotation_data2) &&
|
|
|
|
rev_annotation_data1.CanCombineWith(rev_annotation_data2))
|
2016-12-06 15:30:46 -05:00
|
|
|
{
|
2017-09-25 09:37:11 -04:00
|
|
|
BOOST_ASSERT(!(graph.GetEdgeData(forward_e1).reversed &&
|
|
|
|
graph.GetEdgeData(reverse_e1).reversed));
|
2016-12-06 15:30:46 -05:00
|
|
|
/*
|
|
|
|
* Remember Lane Data for compressed parts. This handles scenarios where lane-data
|
|
|
|
* is
|
|
|
|
* only kept up until a traffic light.
|
|
|
|
*
|
|
|
|
* | |
|
|
|
|
* ---------------- |
|
|
|
|
* -^ | |
|
|
|
|
* ----------- |
|
|
|
|
* -v | |
|
|
|
|
* --------------- |
|
|
|
|
* | |
|
|
|
|
*
|
|
|
|
* u ------- v ---- w
|
|
|
|
*
|
|
|
|
* Since the edge is compressable, we can transfer:
|
|
|
|
* "left|right" (uv) and "" (uw) into a string with "left|right" (uw) for the
|
|
|
|
* compressed
|
|
|
|
* edge.
|
|
|
|
* Doing so, we might mess up the point from where the lanes are shown. It should be
|
|
|
|
* reasonable, since the announcements have to come early anyhow. So there is a
|
|
|
|
* potential danger in here, but it saves us from adding a lot of additional edges
|
|
|
|
* for
|
|
|
|
* turn-lanes. Without this,we would have to treat any turn-lane beginning/ending
|
|
|
|
* just
|
|
|
|
* like a barrier.
|
|
|
|
*/
|
2017-09-25 09:37:11 -04:00
|
|
|
const auto selectAnnotation = [&node_data_container](
|
|
|
|
const AnnotationID front_annotation, const AnnotationID back_annotation) {
|
2016-12-06 15:30:46 -05:00
|
|
|
// A lane has tags: u - (front) - v - (back) - w
|
2018-02-05 06:40:18 -05:00
|
|
|
// During contraction, we keep only one of the tags. Usually the one closer
|
|
|
|
// to the intersection is preferred. If its empty, however, we keep the
|
|
|
|
// non-empty one
|
2017-09-25 09:37:11 -04:00
|
|
|
if (node_data_container[back_annotation].lane_description_id ==
|
|
|
|
INVALID_LANE_DESCRIPTIONID)
|
|
|
|
return front_annotation;
|
|
|
|
return back_annotation;
|
2016-12-06 15:30:46 -05:00
|
|
|
};
|
2017-09-25 09:37:11 -04:00
|
|
|
|
|
|
|
graph.GetEdgeData(forward_e1).annotation_data = selectAnnotation(
|
|
|
|
fwd_edge_data1.annotation_data, fwd_edge_data2.annotation_data);
|
|
|
|
graph.GetEdgeData(reverse_e1).annotation_data = selectAnnotation(
|
|
|
|
rev_edge_data1.annotation_data, rev_edge_data2.annotation_data);
|
|
|
|
graph.GetEdgeData(forward_e2).annotation_data = selectAnnotation(
|
|
|
|
fwd_edge_data2.annotation_data, fwd_edge_data1.annotation_data);
|
|
|
|
graph.GetEdgeData(reverse_e2).annotation_data = selectAnnotation(
|
|
|
|
rev_edge_data2.annotation_data, rev_edge_data1.annotation_data);
|
2017-06-22 10:58:22 -04:00
|
|
|
|
2017-07-20 08:03:39 -04:00
|
|
|
/*
|
2017-06-22 10:58:22 -04:00
|
|
|
// Do not compress edge if it crosses a traffic signal.
|
|
|
|
// This can't be done in CanCombineWith, becase we only store the
|
2017-07-20 08:03:39 -04:00
|
|
|
// traffic signals in the `traffic signal` list, which EdgeData
|
2017-06-22 10:58:22 -04:00
|
|
|
// doesn't have access to.
|
2017-07-20 08:03:39 -04:00
|
|
|
*/
|
|
|
|
const bool has_node_penalty = traffic_signals.find(node_v) != traffic_signals.end();
|
|
|
|
boost::optional<EdgeDuration> node_duration_penalty = boost::none;
|
|
|
|
boost::optional<EdgeWeight> node_weight_penalty = boost::none;
|
2017-06-22 10:58:22 -04:00
|
|
|
if (has_node_penalty)
|
2017-07-20 08:03:39 -04:00
|
|
|
{
|
|
|
|
// we cannot handle this as node penalty, if it depends on turn direction
|
2017-09-25 09:37:11 -04:00
|
|
|
if (fwd_edge_data1.flags.restricted != fwd_edge_data2.flags.restricted)
|
2017-07-20 08:03:39 -04:00
|
|
|
continue;
|
|
|
|
|
2017-08-15 10:53:27 -04:00
|
|
|
// generate an artifical turn for the turn penalty generation
|
2018-01-24 15:39:55 -05:00
|
|
|
std::vector<ExtractionTurnLeg> roads_on_the_right;
|
|
|
|
std::vector<ExtractionTurnLeg> roads_on_the_left;
|
|
|
|
ExtractionTurn extraction_turn(0,
|
|
|
|
2,
|
|
|
|
false,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
TRAVEL_MODE_DRIVING,
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
2018-02-07 05:39:02 -05:00
|
|
|
0,
|
2018-01-24 15:39:55 -05:00
|
|
|
false,
|
|
|
|
TRAVEL_MODE_DRIVING,
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
2018-02-07 05:39:02 -05:00
|
|
|
0,
|
2018-01-24 15:39:55 -05:00
|
|
|
roads_on_the_right,
|
|
|
|
roads_on_the_left);
|
2017-07-20 08:03:39 -04:00
|
|
|
scripting_environment.ProcessTurn(extraction_turn);
|
|
|
|
node_duration_penalty = extraction_turn.duration * 10;
|
|
|
|
node_weight_penalty = extraction_turn.weight * weight_multiplier;
|
|
|
|
}
|
2017-06-22 10:58:22 -04:00
|
|
|
|
|
|
|
// Get weights before graph is modified
|
|
|
|
const auto forward_weight1 = fwd_edge_data1.weight;
|
|
|
|
const auto forward_weight2 = fwd_edge_data2.weight;
|
|
|
|
const auto forward_duration1 = fwd_edge_data1.duration;
|
|
|
|
const auto forward_duration2 = fwd_edge_data2.duration;
|
|
|
|
|
|
|
|
BOOST_ASSERT(0 != forward_weight1);
|
|
|
|
BOOST_ASSERT(0 != forward_weight2);
|
|
|
|
|
|
|
|
const auto reverse_weight1 = rev_edge_data1.weight;
|
|
|
|
const auto reverse_weight2 = rev_edge_data2.weight;
|
|
|
|
const auto reverse_duration1 = rev_edge_data1.duration;
|
|
|
|
const auto reverse_duration2 = rev_edge_data2.duration;
|
|
|
|
|
|
|
|
BOOST_ASSERT(0 != reverse_weight1);
|
|
|
|
BOOST_ASSERT(0 != reverse_weight2);
|
|
|
|
|
|
|
|
// add weight of e2's to e1
|
|
|
|
graph.GetEdgeData(forward_e1).weight += forward_weight2;
|
|
|
|
graph.GetEdgeData(reverse_e1).weight += reverse_weight2;
|
|
|
|
|
|
|
|
// add duration of e2's to e1
|
|
|
|
graph.GetEdgeData(forward_e1).duration += forward_duration2;
|
|
|
|
graph.GetEdgeData(reverse_e1).duration += reverse_duration2;
|
|
|
|
|
2017-07-20 08:03:39 -04:00
|
|
|
if (node_weight_penalty && node_duration_penalty)
|
|
|
|
{
|
|
|
|
graph.GetEdgeData(forward_e1).weight += *node_weight_penalty;
|
|
|
|
graph.GetEdgeData(reverse_e1).weight += *node_weight_penalty;
|
|
|
|
graph.GetEdgeData(forward_e1).duration += *node_duration_penalty;
|
|
|
|
graph.GetEdgeData(reverse_e1).duration += *node_duration_penalty;
|
|
|
|
}
|
|
|
|
|
2017-06-22 10:58:22 -04:00
|
|
|
// extend e1's to targets of e2's
|
|
|
|
graph.SetTarget(forward_e1, node_w);
|
|
|
|
graph.SetTarget(reverse_e1, node_u);
|
2016-12-06 15:30:46 -05:00
|
|
|
|
|
|
|
// remove e2's (if bidir, otherwise only one)
|
|
|
|
graph.DeleteEdge(node_v, forward_e2);
|
|
|
|
graph.DeleteEdge(node_v, reverse_e2);
|
|
|
|
|
|
|
|
// update any involved turn restrictions
|
2017-07-06 11:09:24 -04:00
|
|
|
restriction_compressor.Compress(node_u, node_v, node_w);
|
2016-12-06 15:30:46 -05:00
|
|
|
|
|
|
|
// store compressed geometry in container
|
2016-05-12 12:50:10 -04:00
|
|
|
geometry_compressor.CompressEdge(forward_e1,
|
|
|
|
forward_e2,
|
|
|
|
node_v,
|
|
|
|
node_w,
|
|
|
|
forward_weight1,
|
|
|
|
forward_weight2,
|
|
|
|
forward_duration1,
|
2017-07-20 08:03:39 -04:00
|
|
|
forward_duration2,
|
|
|
|
node_weight_penalty,
|
|
|
|
node_duration_penalty);
|
2016-05-12 12:50:10 -04:00
|
|
|
geometry_compressor.CompressEdge(reverse_e1,
|
|
|
|
reverse_e2,
|
|
|
|
node_v,
|
|
|
|
node_u,
|
|
|
|
reverse_weight1,
|
|
|
|
reverse_weight2,
|
|
|
|
reverse_duration1,
|
2017-07-20 08:03:39 -04:00
|
|
|
reverse_duration2,
|
|
|
|
node_weight_penalty,
|
|
|
|
node_duration_penalty);
|
2016-12-06 15:30:46 -05:00
|
|
|
}
|
2015-06-23 19:55:30 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PrintStatistics(original_number_of_nodes, original_number_of_edges, graph);
|
2016-03-03 21:48:39 -05:00
|
|
|
|
|
|
|
// Repeate the loop, but now add all edges as uncompressed values.
|
|
|
|
// The function AddUncompressedEdge does nothing if the edge is already
|
|
|
|
// in the CompressedEdgeContainer.
|
|
|
|
for (const NodeID node_u : util::irange(0u, original_number_of_nodes))
|
|
|
|
{
|
|
|
|
for (const auto edge_id : util::irange(graph.BeginEdges(node_u), graph.EndEdges(node_u)))
|
|
|
|
{
|
|
|
|
const EdgeData &data = graph.GetEdgeData(edge_id);
|
|
|
|
const NodeID target = graph.GetTarget(edge_id);
|
2016-05-12 12:50:10 -04:00
|
|
|
geometry_compressor.AddUncompressedEdge(edge_id, target, data.weight, data.duration);
|
2016-03-03 21:48:39 -05:00
|
|
|
}
|
|
|
|
}
|
2015-06-23 19:55:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void GraphCompressor::PrintStatistics(unsigned original_number_of_nodes,
|
|
|
|
unsigned original_number_of_edges,
|
2016-01-05 10:51:13 -05:00
|
|
|
const util::NodeBasedDynamicGraph &graph) const
|
2015-06-23 19:55:30 -04:00
|
|
|
{
|
|
|
|
|
|
|
|
unsigned new_node_count = 0;
|
|
|
|
unsigned new_edge_count = 0;
|
|
|
|
|
2016-01-05 10:51:13 -05:00
|
|
|
for (const auto i : util::irange(0u, graph.GetNumberOfNodes()))
|
2015-06-23 19:55:30 -04:00
|
|
|
{
|
|
|
|
if (graph.GetOutDegree(i) > 0)
|
|
|
|
{
|
|
|
|
++new_node_count;
|
|
|
|
new_edge_count += (graph.EndEdges(i) - graph.BeginEdges(i));
|
|
|
|
}
|
|
|
|
}
|
2016-12-06 15:30:46 -05:00
|
|
|
util::Log() << "Node compression ratio: " << new_node_count / (double)original_number_of_nodes;
|
|
|
|
util::Log() << "Edge compression ratio: " << new_edge_count / (double)original_number_of_edges;
|
2015-06-23 19:55:30 -04:00
|
|
|
}
|
2016-01-05 10:51:13 -05:00
|
|
|
}
|
|
|
|
}
|