inline std::uint8_t getLaneCountAtIntersection(const NodeID intersection_node, const util::NodeBasedDynamicGraph &node_based_graph) { std::uint8_t lanes = 0; for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(intersection_node)) lanes = std::max( lanes, node_based_graph.GetEdgeData(onto_edge).road_classification.GetNumberOfLanes()); return lanes; }
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::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; }
inline bool isThroughStreet(const std::size_t index, const IntersectionType &intersection, const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table) { const auto &data_at_index = node_data_container.GetAnnotation( node_based_graph.GetEdgeData(intersection[index].eid).annotation_data); if (data_at_index.name_id == EMPTY_NAMEID) return false; // a through street cannot start at our own position -> index 1 for (std::size_t road_index = 1; road_index < intersection.size(); ++road_index) { if (road_index == index) continue; const auto &road = intersection[road_index]; const auto &road_data = node_data_container.GetAnnotation( node_based_graph.GetEdgeData(road.eid).annotation_data); // roads have a near straight angle (180 degree) const bool is_nearly_straight = angularDeviation(road.angle, intersection[index].angle) > (STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE); const bool have_same_name = HaveIdenticalNames( data_at_index.name_id, road_data.name_id, name_table, street_name_suffix_table); const bool have_same_category = node_based_graph.GetEdgeData(intersection[index].eid).flags.road_classification == node_based_graph.GetEdgeData(road.eid).flags.road_classification; if (is_nearly_straight && have_same_name && have_same_category) return true; } return false; }
std::uint8_t Intersection::getHighestConnectedLaneCount(const util::NodeBasedDynamicGraph &graph) const { BOOST_ASSERT(valid()); // non empty() const std::function<std::uint8_t(const ConnectedRoad &)> to_lane_count = [&](const ConnectedRoad &road) { return graph.GetEdgeData(road.eid).road_classification.GetNumberOfLanes(); }; std::uint8_t max_lanes = 0; const auto extract_maximal_value = [&max_lanes](std::uint8_t value) { max_lanes = std::max(max_lanes, value); return false; }; const auto view = *this | boost::adaptors::transformed(to_lane_count); boost::range::find_if(view, extract_maximal_value); return max_lanes; }
boost::optional<EdgeID> SelectRoadByNameOnlyChoiceAndStraightness:: operator()(const NodeID /*nid*/, const EdgeID /*via_edge_id*/, const IntersectionView &intersection, const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container) const { BOOST_ASSERT(!intersection.empty()); const auto comparator = [&](const IntersectionViewData &lhs, const IntersectionViewData &rhs) { // the score of an elemnt results in an ranking preferring valid entries, if required over // invalid requested name_ids over non-requested narrow deviations over non-narrow const auto score = [&](const IntersectionViewData &road) { double result_score = 0; // since angular deviation is limited by 0-180, we add 360 for invalid if (requires_entry && !road.entry_allowed) result_score += 360.; // 180 for undesired name-ids if (desired_name_id != node_data_container .GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data) .name_id) result_score += 180; return result_score + angularDeviation(road.angle, STRAIGHT_ANGLE); }; return score(lhs) < score(rhs); }; const auto min_element = std::min_element(std::next(std::begin(intersection)), std::end(intersection), comparator); if (min_element == intersection.end() || (requires_entry && !min_element->entry_allowed)) return {}; else return (*min_element).eid; }
inline auto makeExtractLanesForRoad(const util::NodeBasedDynamicGraph &node_based_graph) { return [&node_based_graph](const auto &road) { return node_based_graph.GetEdgeData(road.eid).road_classification.GetNumberOfLanes(); }; }
void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes, const std::unordered_set<NodeID> &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); // 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); } } }
bool findPreviousIntersection(const NodeID node_v, const EdgeID via_edge, const Intersection intersection, const TurnAnalysis &turn_analysis, const util::NodeBasedDynamicGraph &node_based_graph, // output parameters NodeID &result_node, EdgeID &result_via_edge, Intersection &result_intersection) { /* We need to find the intersection that is located prior to via_edge. * * NODE_U -> PREVIOUS_ID -> NODE_V -> VIA_EDGE -> NODE_W:INTERSECTION * NODE_U? <- STRAIGHTMOST <- NODE_V <- UTURN * NODE_U? -> UTURN == PREVIOUSE_ID? -> NODE_V -> VIA_EDGE * * To do so, we first get the intersection atNODE and find the straightmost turn from that * node. This will result in NODE_X. The uturn in the intersection at NODE_X should be * PREVIOUS_ID. To verify that find, we check the intersection using our PREVIOUS_ID candidate * to check the intersection at NODE for via_edge */ const constexpr double COMBINE_DISTANCE_CUTOFF = 30; // we check if via-edge is too short. In this case the previous turn cannot influence the turn // at via_edge and the intersection at NODE_W if (node_based_graph.GetEdgeData(via_edge).distance > COMBINE_DISTANCE_CUTOFF) return false; // Node -> Via_Edge -> Intersection[0 == UTURN] -> reverse_of(via_edge) -> Intersection at node // (looking at the reverse direction). const auto node_w = node_based_graph.GetTarget(via_edge); const auto u_turn_at_node_w = intersection[0].turn.eid; const auto node_v_reverse_intersection = turn_analysis.getIntersection(node_w, u_turn_at_node_w); // Continue along the straightmost turn. If there is no straight turn, we cannot find a valid // previous intersection. const auto straightmost_at_v_in_reverse = findClosestTurn(node_v_reverse_intersection, STRAIGHT_ANGLE); if (angularDeviation(straightmost_at_v_in_reverse->turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) return false; const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->turn.eid); const auto node_u_reverse_intersection = turn_analysis.getIntersection(node_v, straightmost_at_v_in_reverse->turn.eid); // now check that the u-turn at the given intersection connects to via-edge // The u-turn at the now found intersection should, hopefully, represent the previous edge. result_node = node_u; result_via_edge = node_u_reverse_intersection[0].turn.eid; // if the edge is not traversable, we obviously don't have a previous intersection or couldn't // find it. if (node_based_graph.GetEdgeData(result_via_edge).reversed) return false; result_intersection = turn_analysis.getIntersection(node_u, result_via_edge); const auto check_via_edge = findClosestTurn(result_intersection, STRAIGHT_ANGLE)->turn.eid; if (check_via_edge != via_edge) return false; result_intersection = turn_analysis.assignTurnTypes(node_u, result_via_edge, std::move(result_intersection)); return true; }
boost::optional<EdgeID> SelectStraightmostRoadByNameAndOnlyChoice:: operator()(const NodeID /*nid*/, const EdgeID /*via_edge_id*/, const IntersectionView &intersection, const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container) const { BOOST_ASSERT(!intersection.empty()); if (intersection.size() == 1) return {}; const auto comparator = [&](const IntersectionViewData &lhs, const IntersectionViewData &rhs) { // the score of an elemnt results in an ranking preferring valid entries, if required over // invalid requested name_ids over non-requested narrow deviations over non-narrow const auto score = [&](const IntersectionViewData &road) { double result_score = 0; // since angular deviation is limited by 0-180, we add 360 for invalid if (requires_entry && !road.entry_allowed) result_score += 360.; // 180 for undesired name-ids if (desired_name_id != node_data_container .GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data) .name_id) result_score += 180; return result_score + angularDeviation(road.angle, STRAIGHT_ANGLE); }; return score(lhs) < score(rhs); }; const auto count_desired_name = std::count_if(std::begin(intersection), std::end(intersection), [&](const auto &road) { return node_data_container .GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data) .name_id == desired_name_id; }); if (count_desired_name > 2) return {}; const auto min_element = std::min_element(std::next(std::begin(intersection)), std::end(intersection), comparator); const auto is_valid_choice = !requires_entry || min_element->entry_allowed; if (!is_valid_choice) return {}; // only road exiting or continuing in the same general direction const auto has_valid_angle = ((intersection.size() == 2 || intersection.findClosestTurn(STRAIGHT_ANGLE) == min_element) && angularDeviation(min_element->angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) && angularDeviation(initial_bearing, min_element->perceived_bearing) < NARROW_TURN_ANGLE; if (has_valid_angle) return (*min_element).eid; // in some cases, stronger turns are appropriate. We allow turns of just a bit over 90 degrees, // if it's not a end of road situation. These angles come into play where roads split into dual // carriage-ways. // // e - - f // a - - - - b // c - - d // | // g // // is technically // // // a - - - - b (ce) - - (fg) // | // g const auto is_only_choice_with_same_name = count_desired_name <= 2 && // <= in case we come from a bridge, otherwise we have a u-turn // and the outgoing edge node_data_container .GetAnnotation(node_based_graph.GetEdgeData(min_element->eid).annotation_data) .name_id == desired_name_id && angularDeviation(min_element->angle, STRAIGHT_ANGLE) < 100; // don't do crazy turns // do not allow major turns in the road, if other similar turns are present // e.g.a turn at the end of the road: // // a - - a - - a - b - b // | // a - - a // | // c // // Such a turn can never be part of a merge // We check if there is a similar turn to the other side. If such a turn exists, we consider the // continuation of the road not possible if (stop_on_ambiguous_turns && util::angularDeviation(STRAIGHT_ANGLE, min_element->angle) > GROUP_ANGLE) { auto opposite = intersection.findClosestTurn(util::bearing::reverse(min_element->angle)); auto opposite_deviation = util::angularDeviation(min_element->angle, opposite->angle); // d - - - - c - - - -e // | // | // a - - - - b // from b-c onto min_element d with opposite side e if (opposite_deviation > (180 - FUZZY_ANGLE_DIFFERENCE)) return {}; // e // | // a - - - - b - - - - -d // doing a left turn while straight is a choice auto const best = intersection.findClosestTurn(STRAIGHT_ANGLE); if (util::angularDeviation(best->angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE) return {}; } return is_only_choice_with_same_name ? boost::optional<EdgeID>(min_element->eid) : boost::none; }
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, std::vector<UnresolvedManeuverOverride> &maneuver_overrides, util::NodeBasedDynamicGraph &graph, const std::vector<NodeBasedEdgeAnnotation> &node_data_container, CompressedEdgeContainer &geometry_compressor) { const unsigned original_number_of_nodes = graph.GetNumberOfNodes(); const unsigned original_number_of_edges = graph.GetNumberOfEdges(); RestrictionCompressor restriction_compressor( turn_restrictions, conditional_turn_restrictions, maneuver_overrides); // 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); std::for_each(conditional_turn_restrictions.begin(), conditional_turn_restrictions.end(), remember_via_nodes); { const auto weight_multiplier = scripting_environment.GetProfileProperties().GetWeightMultiplier(); util::UnbufferedLog log; util::Percent progress(log, 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_via_nodes.count(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); 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]; if (graph.FindEdgeInEitherDirection(node_u, node_w) != SPECIAL_EDGEID) { continue; } // this case can happen if two ways with different names overlap if ((fwd_annotation_data1.name_id != rev_annotation_data1.name_id) || (fwd_annotation_data2.name_id != rev_annotation_data2.name_id)) { continue; } 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)) { BOOST_ASSERT(!(graph.GetEdgeData(forward_e1).reversed && graph.GetEdgeData(reverse_e1).reversed)); /* * 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 selectAnnotation = [&node_data_container]( const AnnotationID front_annotation, const AnnotationID back_annotation) { // 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 (node_data_container[back_annotation].lane_description_id == INVALID_LANE_DESCRIPTIONID) return front_annotation; return back_annotation; }; 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); /* // Do not compress edge if it crosses a traffic signal. // This can't be done in CanCombineWith, becase we only store the // traffic signals in the `traffic signal` list, which EdgeData // doesn't have access to. */ const bool has_node_penalty = traffic_signals.find(node_v) != traffic_signals.end(); EdgeDuration node_duration_penalty = MAXIMAL_EDGE_DURATION; EdgeWeight node_weight_penalty = INVALID_EDGE_WEIGHT; if (has_node_penalty) { // we cannot handle this as node penalty, if it depends on turn direction if (fwd_edge_data1.flags.restricted != fwd_edge_data2.flags.restricted) continue; // generate an artifical turn for the turn penalty generation 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, 0, false, TRAVEL_MODE_DRIVING, false, false, 1, 0, 0, 0, 0, roads_on_the_right, roads_on_the_left); scripting_environment.ProcessTurn(extraction_turn); node_duration_penalty = extraction_turn.duration * 10; node_weight_penalty = extraction_turn.weight * weight_multiplier; } // 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; const auto forward_distance2 = fwd_edge_data2.distance; 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; const auto reverse_distance2 = rev_edge_data2.distance; #ifndef NDEBUG // Because distances are symmetrical, we only need one // per edge - here we double-check that they match // their mirrors. const auto reverse_distance1 = rev_edge_data1.distance; const auto forward_distance1 = fwd_edge_data1.distance; BOOST_ASSERT(forward_distance1 == reverse_distance2); BOOST_ASSERT(forward_distance2 == reverse_distance1); #endif 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; // add distance of e2's to e1 graph.GetEdgeData(forward_e1).distance += forward_distance2; graph.GetEdgeData(reverse_e1).distance += reverse_distance2; if (node_weight_penalty != INVALID_EDGE_WEIGHT && node_duration_penalty != MAXIMAL_EDGE_DURATION) { 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; // Note: no penalties for distances } // extend e1's to targets of e2's graph.SetTarget(forward_e1, node_w); graph.SetTarget(reverse_e1, node_u); // 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_compressor.Compress(node_u, node_v, node_w); // store compressed geometry in container geometry_compressor.CompressEdge(forward_e1, forward_e2, node_v, node_w, forward_weight1, forward_weight2, forward_duration1, forward_duration2, node_weight_penalty, node_duration_penalty); geometry_compressor.CompressEdge(reverse_e1, reverse_e2, node_v, node_u, reverse_weight1, reverse_weight2, reverse_duration1, reverse_duration2, node_weight_penalty, node_duration_penalty); } } } 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.weight, data.duration); } } }