#include "extractor/graph_compressor.hpp" #include "extractor/compressed_edge_container.hpp" #include "extractor/restriction_map.hpp" #include "util/dynamic_graph.hpp" #include "util/node_based_graph.hpp" #include "util/percent.hpp" #include "util/simple_logger.hpp" namespace osrm { namespace extractor { void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, const std::unordered_set &traffic_lights, RestrictionMap &restriction_map, util::NodeBasedDynamicGraph &graph, CompressedEdgeContainer &geometry_compressor) { const unsigned original_number_of_nodes = graph.GetNumberOfNodes(); const unsigned original_number_of_edges = graph.GetNumberOfEdges(); util::Percent progress(original_number_of_nodes); for (const NodeID node_v : util::irange(0u, original_number_of_nodes)) { progress.PrintStatus(node_v); // only contract degree 2 vertices if (2 != graph.GetOutDegree(node_v)) { continue; } // don't contract barrier node if (barrier_nodes.end() != barrier_nodes.find(node_v)) { continue; } // check if v is a via node for a turn restriction, i.e. a 'directed' barrier node if (restriction_map.IsViaNode(node_v)) { continue; } // 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; 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); if (graph.FindEdgeInEitherDirection(node_u, node_w) != SPECIAL_EDGEID) { continue; } // this case can happen if two ways with different names overlap if (fwd_edge_data1.name_id != rev_edge_data1.name_id || fwd_edge_data2.name_id != rev_edge_data2.name_id) { continue; } if (fwd_edge_data1.IsCompatibleTo(fwd_edge_data2) && rev_edge_data1.IsCompatibleTo(rev_edge_data2)) { BOOST_ASSERT(graph.GetEdgeData(forward_e1).name_id == graph.GetEdgeData(reverse_e1).name_id); BOOST_ASSERT(graph.GetEdgeData(forward_e2).name_id == graph.GetEdgeData(reverse_e2).name_id); // Do not compress edge if it crosses a traffic signal. // This can't be done in IsCompatibleTo, becase we only store the // traffic signals in the `traffic_lights` list, which EdgeData // doesn't have access to. const bool has_node_penalty = traffic_lights.find(node_v) != traffic_lights.end(); if (has_node_penalty) continue; // Get distances before graph is modified const int forward_weight1 = graph.GetEdgeData(forward_e1).distance; const int forward_weight2 = graph.GetEdgeData(forward_e2).distance; BOOST_ASSERT(0 != forward_weight1); BOOST_ASSERT(0 != forward_weight2); const int reverse_weight1 = graph.GetEdgeData(reverse_e1).distance; const int reverse_weight2 = graph.GetEdgeData(reverse_e2).distance; BOOST_ASSERT(0 != reverse_weight1); BOOST_ASSERT(0 != reverse_weight2); // add weight of e2's to e1 graph.GetEdgeData(forward_e1).distance += fwd_edge_data2.distance; graph.GetEdgeData(reverse_e1).distance += rev_edge_data2.distance; // extend e1's to targets of e2's graph.SetTarget(forward_e1, node_w); graph.SetTarget(reverse_e1, node_u); /* * 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. */ const auto selectLaneID = [](const LaneDescriptionID front, const LaneDescriptionID back) { // A lane has tags: u - (front) - v - (back) - w // 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 if (back == INVALID_LANE_DESCRIPTIONID) return front; return back; }; graph.GetEdgeData(forward_e1).lane_description_id = selectLaneID(graph.GetEdgeData(forward_e1).lane_description_id, fwd_edge_data2.lane_description_id); graph.GetEdgeData(reverse_e1).lane_description_id = selectLaneID(graph.GetEdgeData(reverse_e1).lane_description_id, rev_edge_data2.lane_description_id); // 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 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); // store compressed geometry in container geometry_compressor.CompressEdge( forward_e1, forward_e2, node_v, node_w, forward_weight1, forward_weight2); geometry_compressor.CompressEdge( reverse_e1, reverse_e2, node_v, node_u, reverse_weight1, reverse_weight2); } } PrintStatistics(original_number_of_nodes, original_number_of_edges, graph); // 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); geometry_compressor.AddUncompressedEdge(edge_id, target, data.distance); } } } void GraphCompressor::PrintStatistics(unsigned original_number_of_nodes, unsigned original_number_of_edges, const util::NodeBasedDynamicGraph &graph) const { unsigned new_node_count = 0; unsigned new_edge_count = 0; for (const auto i : util::irange(0u, graph.GetNumberOfNodes())) { if (graph.GetOutDegree(i) > 0) { ++new_node_count; new_edge_count += (graph.EndEdges(i) - graph.BeginEdges(i)); } } util::SimpleLogger().Write() << "Node compression ratio: " << new_node_count / (double)original_number_of_nodes; util::SimpleLogger().Write() << "Edge compression ratio: " << new_edge_count / (double)original_number_of_edges; } } }