/* * Segregated Roads often merge onto a single intersection. * While technically representing different roads, they are * often looked at as a single road. * Due to the merging, turn Angles seem off, wenn we compute them from the * initial positions. * * b<b<b<b(1)<b<b<b * aaaaa-b * b>b>b>b(2)>b>b>b * * Would be seen as a slight turn going fro a to (2). A Sharp turn going from * (1) to (2). * * In cases like these, we megre this segregated roads into a single road to * end up with a case like: * * aaaaa-bbbbbb * * for the turn representation. * Anything containing the first u-turn in a merge affects all other angles * and is handled separately from all others. */ Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersection) const { const auto getRight = [&](std::size_t index) { return (index + intersection.size() - 1) % intersection.size(); }; const auto mergable = [&](std::size_t first, std::size_t second) -> bool { const auto &first_data = node_based_graph.GetEdgeData(intersection[first].turn.eid); const auto &second_data = node_based_graph.GetEdgeData(intersection[second].turn.eid); return first_data.name_id != INVALID_NAME_ID && first_data.name_id == second_data.name_id && !first_data.roundabout && !second_data.roundabout && first_data.travel_mode == second_data.travel_mode && first_data.road_classification == second_data.road_classification && // compatible threshold angularDeviation(intersection[first].turn.angle, intersection[second].turn.angle) < 60 && first_data.reversed != second_data.reversed; }; const auto merge = [](const ConnectedRoad &first, const ConnectedRoad &second) -> ConnectedRoad { if (!first.entry_allowed) { ConnectedRoad result = second; result.turn.angle = (first.turn.angle + second.turn.angle) / 2; if (first.turn.angle - second.turn.angle > 180) result.turn.angle += 180; if (result.turn.angle > 360) result.turn.angle -= 360; return result; } else { BOOST_ASSERT(!second.entry_allowed); ConnectedRoad result = first; result.turn.angle = (first.turn.angle + second.turn.angle) / 2; if (first.turn.angle - second.turn.angle > 180) result.turn.angle += 180; if (result.turn.angle > 360) result.turn.angle -= 360; return result; } }; if (intersection.size() <= 1) return intersection; const bool is_connected_to_roundabout = [this, &intersection]() { for (const auto &road : intersection) { if (node_based_graph.GetEdgeData(road.turn.eid).roundabout) return true; } return false; }(); // check for merges including the basic u-turn // these result in an adjustment of all other angles if (mergable(0, intersection.size() - 1)) { const double correction_factor = (360 - intersection[intersection.size() - 1].turn.angle) / 2; for (std::size_t i = 1; i + 1 < intersection.size(); ++i) intersection[i].turn.angle += correction_factor; // FIXME if we have a left-sided country, we need to switch this off and enable it below intersection[0] = merge(intersection.front(), intersection.back()); intersection[0].turn.angle = 0; if (is_connected_to_roundabout) { // We are merging a u-turn against the direction of a roundabout // // -----------> roundabout // / \ // out in // // These cases have to be disabled, even if they are not forbidden specifically by a // relation intersection[0].entry_allowed = false; } intersection.pop_back(); } else if (mergable(0, 1)) { const double correction_factor = (intersection[1].turn.angle) / 2; for (std::size_t i = 2; i < intersection.size(); ++i) intersection[i].turn.angle += correction_factor; intersection[0] = merge(intersection[0], intersection[1]); intersection[0].turn.angle = 0; intersection.erase(intersection.begin() + 1); } // a merge including the first u-turn requres an adjustment of the turn angles // therefore these are handled prior to this step for (std::size_t index = 2; index < intersection.size(); ++index) { if (mergable(index, getRight(index))) { intersection[getRight(index)] = merge(intersection[getRight(index)], intersection[index]); intersection.erase(intersection.begin() + index); --index; } } const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) { return first.turn.angle < second.turn.angle; }; std::sort(std::begin(intersection), std::end(intersection), ByAngle); return intersection; }
std::pair<util::guidance::EntryClass, util::guidance::BearingClass> classifyIntersection(Intersection intersection) { if (intersection.empty()) return {}; std::sort(intersection.begin(), intersection.end(), [](const ConnectedRoad &left, const ConnectedRoad &right) { return left.bearing < right.bearing; }); util::guidance::EntryClass entry_class; util::guidance::BearingClass bearing_class; const bool canBeDiscretized = [&]() { if (intersection.size() <= 1) return true; DiscreteBearing last_discrete_bearing = util::guidance::BearingClass::getDiscreteBearing( std::round(intersection.back().bearing)); for (const auto road : intersection) { const DiscreteBearing discrete_bearing = util::guidance::BearingClass::getDiscreteBearing(std::round(road.bearing)); if (discrete_bearing == last_discrete_bearing) return false; last_discrete_bearing = discrete_bearing; } return true; }(); // finally transfer data to the entry/bearing classes std::size_t number = 0; if (canBeDiscretized) { if (util::guidance::BearingClass::getDiscreteBearing(intersection.back().bearing) < util::guidance::BearingClass::getDiscreteBearing(intersection.front().bearing)) { intersection.insert(intersection.begin(), intersection.back()); intersection.pop_back(); } for (const auto &road : intersection) { if (road.entry_allowed) entry_class.activate(number); auto discrete_bearing_class = util::guidance::BearingClass::getDiscreteBearing(std::round(road.bearing)); bearing_class.add(std::round(discrete_bearing_class * util::guidance::BearingClass::discrete_step_size)); ++number; } } else { for (const auto &road : intersection) { if (road.entry_allowed) entry_class.activate(number); bearing_class.add(std::round(road.bearing)); ++number; } } return std::make_pair(entry_class, bearing_class); }