diff --git a/generator/generator_tests/road_access_test.cpp b/generator/generator_tests/road_access_test.cpp index 4eb8b41909..07bfdaa7f3 100644 --- a/generator/generator_tests/road_access_test.cpp +++ b/generator/generator_tests/road_access_test.cpp @@ -1,8 +1,6 @@ #include "testing/testing.hpp" #include "generator/generator_tests/common.hpp" -#include "generator/generator_tests_support/routing_helpers.hpp" -#include "generator/generator_tests_support/test_mwm_builder.hpp" #include "generator/intermediate_data.hpp" #include "generator/osm2type.hpp" #include "generator/road_access_generator.hpp" @@ -14,10 +12,7 @@ #include "geometry/point2d.hpp" -#include "coding/files_container.hpp" - #include -#include #include #include @@ -299,6 +294,30 @@ UNIT_CLASS_TEST(TestAccessFixture, CarPermit) make_pair(RoadAccess::Type::Private, RoadAccess::Confidence::Sure), ()); } +// https://www.openstreetmap.org/way/797145238#map=19/-34.61801/-58.36501 +UNIT_CLASS_TEST(TestAccessFixture, HgvDesignated) +{ + CreateCollectors(); + AddWay(MakeOsmElementWithNodes(1 /* id */, + {{"highway", "motorway"}, {"access", "no"}, {"emergency", "yes"}, + {"bus", "yes"}, {"hgv", "designated"}, {"motor_vehicle", "yes"}}, + OsmElement::EntityType::Way, {1, 2})); + AddWay(MakeOsmElementWithNodes(2 /* id */, + {{"highway", "motorway"}, {"access", "no"}, {"emergency", "yes"}, + {"bus", "yes"}, {"hgv", "designated"}}, + OsmElement::EntityType::Way, {2, 3})); + Finish(); + + auto const noSure = make_pair(RoadAccess::Type::No, RoadAccess::Confidence::Sure); + TEST_EQUAL(Get(VehicleType::Pedestrian).GetAccessWithoutConditional(1), noSure, ()); + TEST_EQUAL(Get(VehicleType::Bicycle).GetAccessWithoutConditional(1), noSure, ()); + + TEST_EQUAL(Get(VehicleType::Car).GetAccessWithoutConditional(1), + make_pair(RoadAccess::Type::Yes, RoadAccess::Confidence::Sure), ()); + TEST_EQUAL(Get(VehicleType::Car).GetAccessWithoutConditional(2), + make_pair(RoadAccess::Type::No, RoadAccess::Confidence::Sure), ()); +} + UNIT_CLASS_TEST(TestAccessFixture, Merge) { CreateCollectors(3); @@ -479,10 +498,43 @@ UNIT_CLASS_TEST(TestAccessFixture, WinterRoads) { auto const & ra = Get(vehicle); for (uint64_t id = 1; id <= 2; ++id) - TEST_GREATER(ra.GetWayToAccessConditional().at(id).Size(), 0, ()); + { + auto const & wac = ra.GetWayToAccessConditional(); + auto const it = wac.find(id); + TEST(it != wac.end(), (id, vehicle)); + TEST_GREATER(it->second.Size(), 0, ()); + } } /// @todo Add more timing tests using /// GetUnixtimeByDate(2020, Month::Apr, Weekday::Monday, 11 /* hh */, 50 /* mm */) } + +UNIT_CLASS_TEST(TestAccessFixture, Locked) +{ + CreateCollectors(); + + AddWay(MakeOsmElementWithNodes(1 /* id */, {{"highway", "service"}} /* tags */, + OsmElement::EntityType::Way, {10, 11, 12, 13})); + AddWay(MakeOsmElementWithNodes(2 /* id */, {{"highway", "secondary"}} /* tags */, + OsmElement::EntityType::Way, {20, 21, 22, 23})); + + AddNode(MakeOsmElement(11 /* id */, {{"barrier", "gate"}, {"locked", "yes"}}, + OsmElement::EntityType::Node)); + AddNode(MakeOsmElement(21 /* id */, {{"barrier", "gate"}, {"locked", "yes"}, {"access", "permissive"}}, + OsmElement::EntityType::Node)); + + Finish(); + + auto const privateSure = make_pair(RoadAccess::Type::Private, RoadAccess::Confidence::Sure); + auto const yesSure = make_pair(RoadAccess::Type::Yes, RoadAccess::Confidence::Sure); + + for (VehicleType t : {VehicleType::Car, VehicleType::Bicycle, VehicleType::Pedestrian}) + { + auto const & vehicle = Get(t); + TEST_EQUAL(vehicle.GetAccessWithoutConditional({1, 1}), privateSure, ()); + TEST_EQUAL(vehicle.GetAccessWithoutConditional({2, 1}), yesSure, (t)); + } +} + } // namespace road_access_test diff --git a/generator/road_access_generator.cpp b/generator/road_access_generator.cpp index 6bcc930be7..ca9b7f6ce1 100644 --- a/generator/road_access_generator.cpp +++ b/generator/road_access_generator.cpp @@ -1,13 +1,10 @@ #include "generator/road_access_generator.hpp" #include "generator/feature_builder.hpp" -#include "generator/final_processor_utils.hpp" -#include "generator/intermediate_data.hpp" #include "generator/routing_helpers.hpp" #include "routing/road_access.hpp" #include "routing/road_access_serialization.hpp" -#include "routing/routing_helpers.hpp" #include "coding/file_writer.hpp" #include "coding/files_container.hpp" @@ -42,7 +39,7 @@ vector const kCarAccessConditionalTags = { }; vector const kDefaultAccessConditionalTags = { - "access:conditional" + "access:conditional", "locked:conditional" }; vector const kPedestrianAccessConditionalTags = { @@ -146,6 +143,7 @@ TagMapping const kDefaultTagMapping = { {OsmElement::Tag("access", "military"), RoadAccess::Type::No}, {OsmElement::Tag("access", "agricultural"), RoadAccess::Type::Private}, {OsmElement::Tag("access", "forestry"), RoadAccess::Type::Private}, + {OsmElement::Tag("locked", "yes"), RoadAccess::Type::Locked}, }; // Removed secondary, tertiary from car list. Example https://www.openstreetmap.org/node/8169922700 @@ -170,6 +168,7 @@ std::set const kHighwaysWhereIgnoreBarriersWithoutAccessBicycle {"highway", "secondary_link"}, {"highway", "tertiary"}, {"highway", "tertiary_link"}, + {"highway", "cycleway"}, // Bicycle barriers without access on cycleway are ignored :) }; // motorway_junction blocks not only highway link, but main road also. @@ -190,19 +189,23 @@ std::set const kHighwaysWhereIgnoreAccessDestination = { auto const kEmptyAccess = RoadAccess::Type::Count; -// If |elem| has access tag from |mapping|, returns corresponding RoadAccess::Type. -// Tags in |mapping| should be mutually exclusive. Caller is responsible for that. If there are -// multiple access tags from |mapping| in |elem|, returns RoadAccess::Type for any of them. -// Returns RoadAccess::Type::Count if |elem| has no access tags from |mapping|. +// Access can be together with locked (not exclusive). RoadAccess::Type GetAccessTypeFromMapping(OsmElement const & elem, TagMapping const * mapping) { + bool isLocked = false; for (auto const & tag : elem.m_tags) { auto const it = mapping->find(tag); if (it != mapping->cend()) - return it->second; + { + if (it->second == RoadAccess::Type::Locked) + isLocked = true; + else + return it->second; + } } - return kEmptyAccess; + + return (isLocked ? RoadAccess::Type::Locked : kEmptyAccess); } std::optional> GetTagValueConditionalAccess( @@ -223,11 +226,7 @@ std::optional> GetTagValueConditionalAccess( for (auto const & [tag, access] : kTagToAccessConditional) { if (elem.HasTag(tag.m_key, tag.m_value)) - { - CHECK(!tagsList.back().empty(), ()); - auto const anyAccessConditionalTag = tagsList.back().back(); - return std::make_pair(anyAccessConditionalTag, access); - } + return std::make_pair(kDefaultAccessConditionalTags.front(), access); } return {}; @@ -301,10 +300,14 @@ void RoadAccessTagProcessor::Process(OsmElement const & elem) auto const accessType = GetAccessTypeFromMapping(elem, tagMapping); if (accessType != kEmptyAccess) { - if (accessType == RoadAccess::Type::Permit) - isPermit = true; - else + switch (accessType) { + case RoadAccess::Type::Permit: + isPermit = true; + break; + case RoadAccess::Type::Locked: + return RoadAccess::Type::Private; + default: // Patch: (permit + access=no) -> private. if (accessType == RoadAccess::Type::No && isPermit) return RoadAccess::Type::Private; @@ -355,7 +358,6 @@ void RoadAccessTagProcessor::Process(OsmElement const & elem) return; // Apply barrier tags if we have no {vehicle = ...}, {access = ...} etc. - /// @todo Should we treat for example cycle_barrier as access=no always? op = getAccessType(m_barrierMappings); if (op != kEmptyAccess) m_barriersWithoutAccessTag.Add(elem.m_id, {elem.m_lat, elem.m_lon}, op); @@ -616,6 +618,7 @@ AccessConditionalTagParser const & AccessConditionalTagParser::Instance() AccessConditionalTagParser::AccessConditionalTagParser() { + // Order is important here starting from most specific (motorcar) to generic (access). m_vehiclesToRoadAccess.push_back(kMotorCarTagMapping); m_vehiclesToRoadAccess.push_back(kMotorVehicleTagMapping); m_vehiclesToRoadAccess.push_back(kVehicleTagMapping); @@ -711,10 +714,14 @@ RoadAccess::Type AccessConditionalTagParser::GetAccessByVehicleAndStringValue( auto const it = vehicleToAccess.find({vehicleFromTag, stringAccessValue}); if (it != vehicleToAccess.end()) { - if (it->second == RoadAccess::Type::Permit) - isPermit = true; - else + switch (it->second) { + case RoadAccess::Type::Permit: + isPermit = true; + break; + case RoadAccess::Type::Locked: + return RoadAccess::Type::Private; + default: // Patch: (permit + access=no) -> private. if (it->second == RoadAccess::Type::No && isPermit) return RoadAccess::Type::Private; @@ -722,6 +729,7 @@ RoadAccess::Type AccessConditionalTagParser::GetAccessByVehicleAndStringValue( } } } + return kEmptyAccess; } diff --git a/routing/road_access.hpp b/routing/road_access.hpp index 9adf1be142..c79b02ce3c 100644 --- a/routing/road_access.hpp +++ b/routing/road_access.hpp @@ -39,10 +39,15 @@ public: // The number of different road types. Count, - /// @todo Generator only, not serialized option. + /// @name Generator only, not serialized options. + /// @{ // https://github.com/organicmaps/organicmaps/issues/2600 // Open only to people who have obtained a permit granting them access, but permit is ordinarily granted. Permit, + // https://github.com/organicmaps/organicmaps/issues/4442 + // locked=yes, will be transformed into Private. + Locked, + /// @} }; enum class Confidence