diff --git a/drape_frontend/rule_drawer.cpp b/drape_frontend/rule_drawer.cpp index 2146dd0c1..b0cca08c1 100644 --- a/drape_frontend/rule_drawer.cpp +++ b/drape_frontend/rule_drawer.cpp @@ -352,18 +352,19 @@ void RuleDrawer::ProcessLineStyle(FeatureType & f, Stylist const & s, TInsertSha if (m_context->IsTrafficEnabled() && m_zoomLevel >= kRoadClass0ZoomLevel) { + using ftypes::HighwayClass; struct Checker { - std::vector m_highwayClasses; + std::vector m_highwayClasses; int m_zoomLevel; df::RoadClass m_roadClass; }; static Checker const checkers[] = { - {{ftypes::HighwayClass::Trunk, ftypes::HighwayClass::Primary}, + {{HighwayClass::Trunk, HighwayClass::Primary}, kRoadClass0ZoomLevel, df::RoadClass::Class0}, - {{ftypes::HighwayClass::Secondary, ftypes::HighwayClass::Tertiary}, + {{HighwayClass::Secondary, HighwayClass::Tertiary}, kRoadClass1ZoomLevel, df::RoadClass::Class1}, - {{ftypes::HighwayClass::LivingStreet, ftypes::HighwayClass::Service}, + {{HighwayClass::LivingStreet, HighwayClass::Service, HighwayClass::ServiceMinor}, kRoadClass2ZoomLevel, df::RoadClass::Class2} }; diff --git a/indexer/ftypes_matcher.cpp b/indexer/ftypes_matcher.cpp index cd0705a96..50cb56634 100644 --- a/indexer/ftypes_matcher.cpp +++ b/indexer/ftypes_matcher.cpp @@ -22,71 +22,76 @@ namespace { class HighwayClasses { - map m_map; + map m_map; public: HighwayClasses() { auto const & c = classif(); - m_map[c.GetTypeByPath({"route", "ferry"})] = ftypes::HighwayClass::Transported; - m_map[c.GetTypeByPath({"route", "shuttle_train"})] = ftypes::HighwayClass::Transported; + m_map[c.GetTypeByPath({"route", "ferry"})] = HighwayClass::Transported; + m_map[c.GetTypeByPath({"route", "shuttle_train"})] = HighwayClass::Transported; - m_map[c.GetTypeByPath({"highway", "motorway"})] = ftypes::HighwayClass::Trunk; - m_map[c.GetTypeByPath({"highway", "motorway_link"})] = ftypes::HighwayClass::Trunk; - m_map[c.GetTypeByPath({"highway", "trunk"})] = ftypes::HighwayClass::Trunk; - m_map[c.GetTypeByPath({"highway", "trunk_link"})] = ftypes::HighwayClass::Trunk; + m_map[c.GetTypeByPath({"highway", "motorway"})] = HighwayClass::Trunk; + m_map[c.GetTypeByPath({"highway", "motorway_link"})] = HighwayClass::Trunk; + m_map[c.GetTypeByPath({"highway", "trunk"})] = HighwayClass::Trunk; + m_map[c.GetTypeByPath({"highway", "trunk_link"})] = HighwayClass::Trunk; - m_map[c.GetTypeByPath({"highway", "primary"})] = ftypes::HighwayClass::Primary; - m_map[c.GetTypeByPath({"highway", "primary_link"})] = ftypes::HighwayClass::Primary; + m_map[c.GetTypeByPath({"highway", "primary"})] = HighwayClass::Primary; + m_map[c.GetTypeByPath({"highway", "primary_link"})] = HighwayClass::Primary; - m_map[c.GetTypeByPath({"highway", "secondary"})] = ftypes::HighwayClass::Secondary; - m_map[c.GetTypeByPath({"highway", "secondary_link"})] = ftypes::HighwayClass::Secondary; + m_map[c.GetTypeByPath({"highway", "secondary"})] = HighwayClass::Secondary; + m_map[c.GetTypeByPath({"highway", "secondary_link"})] = HighwayClass::Secondary; - m_map[c.GetTypeByPath({"highway", "tertiary"})] = ftypes::HighwayClass::Tertiary; - m_map[c.GetTypeByPath({"highway", "tertiary_link"})] = ftypes::HighwayClass::Tertiary; + m_map[c.GetTypeByPath({"highway", "tertiary"})] = HighwayClass::Tertiary; + m_map[c.GetTypeByPath({"highway", "tertiary_link"})] = HighwayClass::Tertiary; - m_map[c.GetTypeByPath({"highway", "unclassified"})] = ftypes::HighwayClass::LivingStreet; - m_map[c.GetTypeByPath({"highway", "residential"})] = ftypes::HighwayClass::LivingStreet; - m_map[c.GetTypeByPath({"highway", "living_street"})] = ftypes::HighwayClass::LivingStreet; - m_map[c.GetTypeByPath({"highway", "road"})] = ftypes::HighwayClass::LivingStreet; + m_map[c.GetTypeByPath({"highway", "unclassified"})] = HighwayClass::LivingStreet; + m_map[c.GetTypeByPath({"highway", "residential"})] = HighwayClass::LivingStreet; + m_map[c.GetTypeByPath({"highway", "living_street"})] = HighwayClass::LivingStreet; + m_map[c.GetTypeByPath({"highway", "road"})] = HighwayClass::LivingStreet; - m_map[c.GetTypeByPath({"highway", "service"})] = ftypes::HighwayClass::Service; - m_map[c.GetTypeByPath({"highway", "track"})] = ftypes::HighwayClass::Service; - m_map[c.GetTypeByPath({"highway", "busway"})] = ftypes::HighwayClass::Service; - m_map[c.GetTypeByPath({"man_made", "pier"})] = ftypes::HighwayClass::Service; + m_map[c.GetTypeByPath({"highway", "service"})] = HighwayClass::Service; + m_map[c.GetTypeByPath({"highway", "track"})] = HighwayClass::Service; + m_map[c.GetTypeByPath({"highway", "busway"})] = HighwayClass::Service; + m_map[c.GetTypeByPath({"man_made", "pier"})] = HighwayClass::Service; - m_map[c.GetTypeByPath({"highway", "pedestrian"})] = ftypes::HighwayClass::Pedestrian; - m_map[c.GetTypeByPath({"highway", "footway"})] = ftypes::HighwayClass::Pedestrian; - m_map[c.GetTypeByPath({"highway", "bridleway"})] = ftypes::HighwayClass::Pedestrian; - m_map[c.GetTypeByPath({"highway", "steps"})] = ftypes::HighwayClass::Pedestrian; - m_map[c.GetTypeByPath({"highway", "cycleway"})] = ftypes::HighwayClass::Pedestrian; - m_map[c.GetTypeByPath({"highway", "path"})] = ftypes::HighwayClass::Pedestrian; - m_map[c.GetTypeByPath({"highway", "construction"})] = ftypes::HighwayClass::Pedestrian; + // 3-level types. + m_map[c.GetTypeByPath({"highway", "service", "driveway"})] = HighwayClass::ServiceMinor; + m_map[c.GetTypeByPath({"highway", "service", "parking_aisle"})] = HighwayClass::ServiceMinor; + + m_map[c.GetTypeByPath({"highway", "pedestrian"})] = HighwayClass::Pedestrian; + m_map[c.GetTypeByPath({"highway", "footway"})] = HighwayClass::Pedestrian; + m_map[c.GetTypeByPath({"highway", "bridleway"})] = HighwayClass::Pedestrian; + m_map[c.GetTypeByPath({"highway", "steps"})] = HighwayClass::Pedestrian; + m_map[c.GetTypeByPath({"highway", "cycleway"})] = HighwayClass::Pedestrian; + m_map[c.GetTypeByPath({"highway", "path"})] = HighwayClass::Pedestrian; + m_map[c.GetTypeByPath({"highway", "construction"})] = HighwayClass::Pedestrian; } - ftypes::HighwayClass Get(uint32_t t) const + HighwayClass Get(uint32_t t) const { auto const it = m_map.find(t); if (it == m_map.cend()) - return ftypes::HighwayClass::Undefined; + return HighwayClass::Undefined; return it->second; } }; -char const * HighwayClassToString(ftypes::HighwayClass const cls) +char const * HighwayClassToString(HighwayClass const cls) { switch (cls) { - case ftypes::HighwayClass::Undefined: return "Undefined"; - case ftypes::HighwayClass::Transported: return "Transported"; - case ftypes::HighwayClass::Trunk: return "Trunk"; - case ftypes::HighwayClass::Primary: return "Primary"; - case ftypes::HighwayClass::Secondary: return "Secondary"; - case ftypes::HighwayClass::Tertiary: return "Tertiary"; - case ftypes::HighwayClass::LivingStreet: return "LivingStreet"; - case ftypes::HighwayClass::Service: return "Service"; - case ftypes::HighwayClass::Pedestrian: return "Pedestrian"; - case ftypes::HighwayClass::Count: return "Count"; + case HighwayClass::Undefined: return "Undefined"; + case HighwayClass::Transported: return "Transported"; + case HighwayClass::Trunk: return "Trunk"; + case HighwayClass::Primary: return "Primary"; + case HighwayClass::Secondary: return "Secondary"; + case HighwayClass::Tertiary: return "Tertiary"; + case HighwayClass::LivingStreet: return "LivingStreet"; + case HighwayClass::Service: return "Service"; + case HighwayClass::ServiceMinor: return "ServiceMinor"; + case HighwayClass::Pedestrian: return "Pedestrian"; + case HighwayClass::Count: return "Count"; } ASSERT(false, ()); return ""; @@ -121,10 +126,13 @@ HighwayClass GetHighwayClass(feature::TypesHolder const & types) for (auto t : types) { - ftype::TruncValue(t, 2); - HighwayClass const hc = highwayClasses.Get(t); - if (hc != HighwayClass::Undefined) - return hc; + for (uint8_t level : {3, 2}) + { + ftype::TruncValue(t, level); + HighwayClass const hc = highwayClasses.Get(t); + if (hc != HighwayClass::Undefined) + return hc; + } } return HighwayClass::Undefined; diff --git a/indexer/ftypes_matcher.hpp b/indexer/ftypes_matcher.hpp index 22fb1b388..c4ea6c25f 100644 --- a/indexer/ftypes_matcher.hpp +++ b/indexer/ftypes_matcher.hpp @@ -635,6 +635,9 @@ enum class HighwayClass Tertiary, LivingStreet, Service, + // OSM highway=service type is widely used even for _significant_ roads. + // Adding a new type to distinguish mapped driveway or parking_aisle. + ServiceMinor, Pedestrian, Transported, // Vehicles are transported by train or ferry. Count // This value is used for internals only. diff --git a/openlr/helpers.cpp b/openlr/helpers.cpp index 40a8150de..2f80fab1a 100644 --- a/openlr/helpers.cpp +++ b/openlr/helpers.cpp @@ -11,8 +11,7 @@ #include #include #include -#include -#include + namespace { @@ -29,6 +28,7 @@ openlr::FunctionalRoadClass HighwayClassToFunctionalRoadClass(ftypes::HighwayCla case ftypes::HighwayClass::Tertiary: return openlr::FunctionalRoadClass::FRC3; case ftypes::HighwayClass::LivingStreet: return openlr::FunctionalRoadClass::FRC4; case ftypes::HighwayClass::Service: return openlr::FunctionalRoadClass::FRC5; + case ftypes::HighwayClass::ServiceMinor: return openlr::FunctionalRoadClass::FRC6; default: return openlr::FunctionalRoadClass::FRC7; } } @@ -45,38 +45,42 @@ optional GetFrcScore(Graph::Edge const & e, FunctionalRoadClass functiona auto const hwClass = infoGetter.Get(e.GetFeatureId()).m_hwClass; + using ftypes::HighwayClass; + switch (functionalRoadClass) { case FunctionalRoadClass::FRC0: // Note. HighwayClass::Trunk means motorway, motorway_link, trunk or trunk_link. - return hwClass == ftypes::HighwayClass::Trunk ? optional(kMaxScoreForFrc) : nullopt; + return hwClass == HighwayClass::Trunk ? optional(kMaxScoreForFrc) : nullopt; case FunctionalRoadClass::FRC1: - return (hwClass == ftypes::HighwayClass::Trunk || hwClass == ftypes::HighwayClass::Primary) + return (hwClass == HighwayClass::Trunk || hwClass == HighwayClass::Primary) ? optional(kMaxScoreForFrc) : nullopt; case FunctionalRoadClass::FRC2: case FunctionalRoadClass::FRC3: - if (hwClass == ftypes::HighwayClass::Secondary || hwClass == ftypes::HighwayClass::Tertiary) + if (hwClass == HighwayClass::Secondary || hwClass == HighwayClass::Tertiary) return optional(kMaxScoreForFrc); - return hwClass == ftypes::HighwayClass::Primary || hwClass == ftypes::HighwayClass::LivingStreet + return hwClass == HighwayClass::Primary || hwClass == HighwayClass::LivingStreet ? optional(0) : nullopt; case FunctionalRoadClass::FRC4: - if (hwClass == ftypes::HighwayClass::LivingStreet || hwClass == ftypes::HighwayClass::Service) + if (hwClass == HighwayClass::LivingStreet || hwClass == HighwayClass::Service) return optional(kMaxScoreForFrc); - return (hwClass == ftypes::HighwayClass::Tertiary || hwClass == ftypes::HighwayClass::Secondary) + return (hwClass == HighwayClass::Tertiary || hwClass == HighwayClass::Secondary) ? optional(0) : nullopt; case FunctionalRoadClass::FRC5: case FunctionalRoadClass::FRC6: case FunctionalRoadClass::FRC7: - return hwClass == ftypes::HighwayClass::LivingStreet || hwClass == ftypes::HighwayClass::Service + return (hwClass == HighwayClass::LivingStreet || + hwClass == HighwayClass::Service || + hwClass == HighwayClass::ServiceMinor) ? optional(kMaxScoreForFrc) : nullopt; diff --git a/routing/car_directions.cpp b/routing/car_directions.cpp index aebbd08c0..92ab32f18 100644 --- a/routing/car_directions.cpp +++ b/routing/car_directions.cpp @@ -6,7 +6,6 @@ #include "geometry/angles.hpp" -#include namespace routing { @@ -175,8 +174,8 @@ bool KeepRoundaboutTurnByHighwayClass(TurnCandidates const & possibleTurns, if (t.m_segment == firstOutgoingSegment) continue; // For roundabouts of Tertiary and less significant class count every road. - // For more significant roundabouts - ignore service roads. - if (turnInfo.m_outgoing->m_highwayClass >= HighwayClass::Tertiary || t.m_highwayClass != HighwayClass::Service) + // For more significant roundabouts - ignore service roads (driveway, parking_aisle). + if (turnInfo.m_outgoing->m_highwayClass >= HighwayClass::Tertiary || t.m_highwayClass != HighwayClass::ServiceMinor) return true; } return false; @@ -303,7 +302,8 @@ bool CanDiscardTurnByHighwayClassOrAngles(CarDirection const routeDirection, continue; // If outgoing route's road is significantly larger than candidate's service road, the candidate can be ignored. - if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForService && candidateRoadClass == HighwayClass::Service) + if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForService && + (candidateRoadClass == HighwayClass::Service || candidateRoadClass == HighwayClass::ServiceMinor)) continue; // If igngoing and outgoing edges are not links @@ -445,7 +445,8 @@ void GetTurnDirectionBasic(IRoutingResult const & result, size_t const outgoingS return; // No turns are needed on transported road. - if (turnInfo.m_ingoing->m_highwayClass == HighwayClass::Transported && turnInfo.m_outgoing->m_highwayClass == HighwayClass::Transported) + if (turnInfo.m_ingoing->m_highwayClass == HighwayClass::Transported && + turnInfo.m_outgoing->m_highwayClass == HighwayClass::Transported) return; Segment firstOutgoingSeg; diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index 42558e427..aa3792ebd 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -93,6 +93,8 @@ UNIT_TEST(Russia_Moscow_TrikotagniAndPohodniRoundabout_TurnTest) Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; + /// @todo Simple highway=service is now included in turns count. + /// Fix OSM on driveway/parking_aisle ? TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 2 /* expectedTurnCount */); @@ -1389,4 +1391,39 @@ UNIT_TEST(UK_Junction_Circular) GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::LeaveRoundAbout); } +UNIT_TEST(Integrated_TurnTest_IncludeServiceRoads) +{ + struct Sample + { + ms::LatLon start, finish; + int expectedTurns; + }; + Sample arr[] = { + // https://github.com/organicmaps/organicmaps/issues/8892 + {{50.128011, 14.7100098}, {50.1283017, 14.7119639}, 3}, + {{50.1283462, 14.7122953}, {50.1280032, 14.7099638}, 3}, + // https://github.com/organicmaps/organicmaps/issues/5888 + {{58.8428062, 5.71619759}, {58.8422583, 5.71672851}, 3}, + // https://github.com/organicmaps/organicmaps/issues/3596 + {{38.7114203, 0.0365096768}, {38.7103102, 0.0349380496}, 2}, + }; + + for (auto const & s : arr) + { + using namespace integration; + TRouteResult const routeResult = CalculateRoute(GetVehicleComponents(VehicleType::Car), + mercator::FromLatLon(s.start), {0., 0.}, + mercator::FromLatLon(s.finish)); + + Route const & route = *routeResult.first; + RouterResultCode const result = routeResult.second; + TEST_EQUAL(result, RouterResultCode::NoError, ()); + + GetNthTurn(route, 0) + .TestValid() + .TestDirection(CarDirection::EnterRoundAbout) + .TestRoundAboutExitNum(s.expectedTurns); + } +} + } // namespace turn_test diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 7cb6a8661..705b734bd 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -4,7 +4,6 @@ #include "indexer/ftypes_matcher.hpp" -#include "base/stl_helpers.hpp" namespace routing { @@ -47,7 +46,8 @@ bool CanDiscardTurnByHighwayClass(std::vector const & turnCandida continue; // If route's road is significantly larger than candidate's service road, the candidate can be ignored. - if (CalcDiffRoadClasses(maxRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForService && candidateRoadClass == HighwayClass::Service) + if (CalcDiffRoadClasses(maxRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForService && + (candidateRoadClass == HighwayClass::Service || candidateRoadClass == HighwayClass::ServiceMinor)) continue; return false; diff --git a/routing/turns_generator_utils.cpp b/routing/turns_generator_utils.cpp index 5ce66a1b0..5c5ce8306 100644 --- a/routing/turns_generator_utils.cpp +++ b/routing/turns_generator_utils.cpp @@ -1,8 +1,11 @@ #include "routing/turns_generator_utils.hpp" -#include "platform/measurement_utils.hpp" -#include "geometry/mercator.hpp" #include "routing/turns.hpp" +#include "platform/measurement_utils.hpp" + +#include "geometry/mercator.hpp" + + namespace routing { namespace turns @@ -19,7 +22,9 @@ bool IsHighway(HighwayClass hwClass, bool isLink) bool IsSmallRoad(HighwayClass hwClass) { return hwClass == HighwayClass::LivingStreet || - hwClass == HighwayClass::Service || hwClass == HighwayClass::Pedestrian; + hwClass == HighwayClass::Service || + hwClass == HighwayClass::ServiceMinor || + hwClass == HighwayClass::Pedestrian; } int CalcDiffRoadClasses(HighwayClass const left, HighwayClass const right) @@ -120,7 +125,8 @@ double CalcEstimatedTimeToPass(double const distanceMeters, HighwayClass const h case HighwayClass::Secondary: speedKmph = 70.0; break; case HighwayClass::Tertiary: speedKmph = 50.0; break; case HighwayClass::LivingStreet: speedKmph = 20.0; break; - case HighwayClass::Service: speedKmph = 10.0; break; + case HighwayClass::Service: + case HighwayClass::ServiceMinor: speedKmph = 10.0; break; case HighwayClass::Pedestrian: speedKmph = 5.0; break; default: speedKmph = 50.0; break; }