diff --git a/generator/generator.pro b/generator/generator.pro index 958b576f5a..c65fbfeac1 100644 --- a/generator/generator.pro +++ b/generator/generator.pro @@ -36,6 +36,7 @@ SOURCES += \ osm_id.cpp \ osm_source.cpp \ region_meta.cpp \ + restrictions.cpp \ routing_generator.cpp \ search_index_builder.cpp \ sponsored_scoring.cpp \ @@ -73,6 +74,7 @@ HEADERS += \ osm_xml_source.hpp \ polygonizer.hpp \ region_meta.hpp \ + restrictions.hpp \ routing_generator.hpp \ search_index_builder.hpp \ sponsored_dataset.hpp \ diff --git a/generator/restrictions.cpp b/generator/restrictions.cpp new file mode 100644 index 0000000000..f0f9ec1246 --- /dev/null +++ b/generator/restrictions.cpp @@ -0,0 +1,220 @@ +#include "generator/intermediate_elements.hpp" +#include "generator/restrictions.hpp" + +#include "base/logging.hpp" +#include "base/stl_helpers.hpp" + +#include "std/algorithm.hpp" +#include "std/fstream.hpp" + +namespace +{ +/// \brief Converts restriction type form string to RestrictionCollector::Type. +/// \returns Fisrt item is a result of conversion. Second item is true +/// if convertion was successful and false otherwise. +pair TagToType(string const & type) +{ + vector const restrictionTypesNo = {"no_right_turn", "no_left_turn", "no_u_turn", + "no_straight_on", "no_entry", "no_exit"}; + vector const restrictionTypesOnly = {"only_right_turn", "only_left_turn", "only_straight_on"}; + + if (find(restrictionTypesNo.cbegin(), restrictionTypesNo.cend(), type) != restrictionTypesNo.cend()) + return make_pair(RestrictionCollector::Type::No, true); + + if (find(restrictionTypesOnly.cbegin(), restrictionTypesOnly.cend(), type) != restrictionTypesOnly.cend()) + return make_pair(RestrictionCollector::Type::Only, true); + + // Unsupported restriction type. + return make_pair(RestrictionCollector::Type::No, false); +} +} // namespace + +RestrictionCollector::FeatureId const RestrictionCollector::kInvalidFeatureId = + numeric_limits::max(); + +RestrictionCollector::Restriction::Restriction(Type type, size_t linkNumber) : m_type(type) +{ + m_links.resize(linkNumber, kInvalidFeatureId); +} + +RestrictionCollector::Restriction::Restriction(Type type, vector const & links) + : m_links(links), m_type(type) +{ +} + +bool RestrictionCollector::Restriction::IsValid() const +{ + for (auto fid : m_links) + { + if (fid == kInvalidFeatureId) + return false; + } + return true; +} + +bool RestrictionCollector::Restriction::operator==(Restriction const & restriction) const +{ + return m_links == restriction.m_links && m_type == restriction.m_type; +} + +bool RestrictionCollector::Restriction::operator<(Restriction const & restriction) const +{ + if (m_type != restriction.m_type) + return m_type < restriction.m_type; + + return m_links < restriction.m_links; +} + +RestrictionCollector::~RestrictionCollector() {} + +void RestrictionCollector::AddRestriction(vector const & links, Type type) +{ + lock_guard lock(m_mutex); + m_restrictions.emplace_back(type, links.size()); + size_t const restrictionCount = m_restrictions.size() - 1; + for (size_t i = 0; i < links.size(); ++i) + m_restrictionIndex.push_back(make_pair(links[i], Index({restrictionCount, i}))); +} + +void RestrictionCollector::AddRestriction(RelationElement const & relationElement) +{ + CHECK_EQUAL(relationElement.GetType(), "restriction", ()); + + // Note. For the time being only line-point-line road restriction is supported. + if (relationElement.nodes.size() != 1 || relationElement.ways.size() != 2) + return; // Unsupported restriction. For example line-line-line. + + // Extracting osm ids of lines and points of the restriction. + auto const findTag = [&relationElement](vector> const & members, string const & tag) + { + auto const it = find_if(members.cbegin(), members.cend(), + [&tag](pair const & p) { return p.second == tag; }); + return it; + }; + + auto const fromIt = findTag(relationElement.ways, "from"); + if (fromIt == relationElement.ways.cend()) + return; // No tag |from| in |relationElement.ways|. + + auto const toIt = findTag(relationElement.ways, "to"); + if (toIt == relationElement.ways.cend()) + return; // No tag |to| in |relationElement.ways|. + + if (findTag(relationElement.nodes,"via") == relationElement.nodes.cend()) + return; // No tag |via| in |relationElement.nodes|. + + // Extracting type of restriction. + auto const tagIt = relationElement.tags.find("restriction"); + if (tagIt == relationElement.tags.end()) + return; // Type of the element is different from "restriction". + + auto typeResult = TagToType(tagIt->second); + if (typeResult.second == false) + return; // Unsupported restriction type. + + // Adding restriction. + Type const type = typeResult.first; + osm::Id const fromOsmId = osm::Id::Way(fromIt->first); + osm::Id const toOsmId = osm::Id::Way(toIt->first); + AddRestriction({fromOsmId, toOsmId}, type); +} + +void RestrictionCollector::AddFeatureId(vector const & osmIds, FeatureId featureId) +{ + // Note. One |featureId| could correspond to several osm ids. + // but for road feature |featureId| corresponds exactly one osm id. + lock_guard lock(m_mutex); + for (osm::Id const & osmId : osmIds) + m_osmIds2FeatureId.insert(make_pair(osmId, featureId)); +} + +bool RestrictionCollector::CheckCorrectness() const +{ + for (Restriction const & restriction : m_restrictions) + { + if (!restriction.IsValid()) + return false; + } + return true; +} + +void RestrictionCollector::RemoveInvalidRestrictions() +{ + m_restrictions.erase(remove_if(m_restrictions.begin(), m_restrictions.end(), + [](Restriction const & r){ return !r.IsValid(); }), + m_restrictions.end()); +} + +void RestrictionCollector::ComposeRestrictions() +{ + // Going throught all osm id saved in |m_restrictionIndex| (mentioned in restrictions). + size_t const restrictionSz = m_restrictions.size(); + for (pair const & osmIdAndIndex : m_restrictionIndex) + { + Index const index = osmIdAndIndex.second; + CHECK_LESS(index.m_restrictionNumber, restrictionSz, ()); + Restriction & restriction = m_restrictions[index.m_restrictionNumber]; + CHECK_LESS(index.m_linkNumber, restriction.m_links.size(), ()); + + osm::Id const osmId = osmIdAndIndex.first; + // Checking if there's an osm id belongs to a restriction is saved only once as feature id. + auto const rangeId = m_osmIds2FeatureId.equal_range(osmId); + if (rangeId.first == rangeId.second) + { + // There's no |osmId| in |m_osmIds2FeatureId| which was mentioned in restrictions. + // It could happend near mwm border when one of a restriction lines is not included in mwm + // but the restriction is included. + continue; + } + if (distance(rangeId.first, rangeId.second) != 1) + continue; // |osmId| mentioned in restrictions was included in more than one feature. + + FeatureId const featureId = rangeId.first->second; + // Adding feature id to restriction coresponded to the osm id. + restriction.m_links[index.m_linkNumber] = featureId; + } + + my::SortUnique(m_restrictions); + // After sorting m_restrictions |m_restrictionIndex| is invalid. + m_restrictionIndex.clear(); +} + +void RestrictionCollector::ComposeRestrictionsAndSave(string const & fullPath) +{ + lock_guard lock(m_mutex); + + ComposeRestrictions(); + RemoveInvalidRestrictions(); + + if (m_restrictions.empty()) + return; + + LOG(LINFO, ("Saving intermediate file with restrictions to", fullPath)); + ofstream ofs(fullPath, std::ofstream::out); + for (Restriction const & r : m_restrictions) + { + ofs << DebugPrint(r.m_type) << ", "; + for (FeatureId fid : r.m_links) + ofs << fid << ", "; + ofs << endl; + } +} + +string DebugPrint(RestrictionCollector::Type const & type) +{ + switch (type) + { + case RestrictionCollector::Type::No: + return "No"; + case RestrictionCollector::Type::Only: + return "Only"; + } + return "Unknown"; +} + +string DebugPrint(RestrictionCollector::Restriction const & restriction) +{ + ostringstream out; + out << "m_links:[" << restriction.m_links << "] m_type:" << DebugPrint(restriction.m_type) << " "; + return out.str(); +} diff --git a/generator/restrictions.hpp b/generator/restrictions.hpp new file mode 100644 index 0000000000..bb18eb451f --- /dev/null +++ b/generator/restrictions.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include "generator/osm_id.hpp" + +#include "std/limits.hpp" +#include "std/mutex.hpp" +#include "std/unordered_map.hpp" +#include "std/utility.hpp" +#include "std/vector.hpp" + +class RelationElement; + +namespace std +{ + template <> struct hash + { + size_t operator()(osm::Id const & id) const + { + return hash()(id.OsmId()); + } + }; +} // namespace std + +/// This class collects all relations with type restriction and save feature ids of +/// their road feature in text file for using later. +class RestrictionCollector +{ + friend void UnitTest_RestrictionTest_ValidCase(); + friend void UnitTest_RestrictionTest_InvalidCase(); +public: + using FeatureId = uint64_t; + static FeatureId const kInvalidFeatureId; + + ~RestrictionCollector(); + + /// \brief Types of road graph restrictions. + /// \note Despite the fact more that 10 restriction tags are present in osm all of them + /// could be split into two categories. + /// * no_left_turn, no_right_turn, no_u_turn and so on go to "No" category. + /// * only_left_turn, only_right_turn and so on go to "Only" category. + /// That's enough to rememeber if + /// * there's only way to pass the junction is driving along the restriction (Only) + /// * driving along the restriction is prohibited (No) + enum class Type + { + No, // Going according such restriction is prohibited. + Only, // Only going according such restriction is permitted + }; + + /// \brief Restriction to modify road graph. + struct Restriction + { + Restriction(Type type, size_t linkNumber); + // Constructor for testing. + Restriction(Type type, vector const & links); + + bool IsValid() const; + bool operator==(Restriction const & restriction) const; + bool operator<(Restriction const & restriction) const; + + vector m_links; + Type m_type; + }; + + /// \brief Addresses a link in vector. + struct Index + { + size_t m_restrictionNumber; // Restriction number in restriction vector. + size_t m_linkNumber; // Link number for a restriction. It's equal to zero or one for most cases. + }; + + /// \brief Coverts |relationElement| to Restriction and adds it to |m_restrictions| and + /// |m_restrictionIndex| if |relationElement| is a restriction. + /// \note For the time being only line-point-line restritions are processed. The other restrictions + /// are ignored. + // @TODO(bykoianko) It's necessary to process all kind of restrictions. + void AddRestriction(RelationElement const & relationElement); + + /// \brief Adds feature id and corresponding vector of |osmIds| to |m_osmId2FeatureId|. + /// \note One feature id (|featureId|) may correspond to several osm ids (|osmIds|). + void AddFeatureId(vector const & osmIds, FeatureId featureId); + + /// \brief Save all restrictions (content of |m_restrictions|) to text file in the following format: + /// * One restriction is saved in one line + /// * Line format is: , , , ... + /// * Example 1: "No, 12345, 12346" + /// * Example 2: "Only, 12349, 12341" + /// \note Most restrictions consist of type and two linear(road) features. + /// \note For the time being only line-point-line restritions are supported. + void ComposeRestrictionsAndSave(string const & fullPath); + +private: + /// \returns true if all feature ids in |m_restrictions| are set to valid value + /// and false otherwise. + /// \note The method may be called after ComposeRestrictions(). + bool CheckCorrectness() const; + + /// \brief removes all restriction with incorrect feature id. + /// \note The method should be called after ComposeRestrictions(). + void RemoveInvalidRestrictions(); + + /// \brief Sets feature id for all restrictions in |m_restrictions|. + void ComposeRestrictions(); + + /// \brief Adds a restriction (vector of osm::Id). + /// \param links is osm ids of restriction links + /// \param type is a type of restriction + /// \note This method should be called to add a restriction when feature ids of the restriction + /// are unknown. The feature ids should be set later with a call of |SetFeatureId(...)| method. + void AddRestriction(vector const & links, Type type); + + mutex m_mutex; + vector m_restrictions; + vector> m_restrictionIndex; + + unordered_multimap m_osmIds2FeatureId; +}; + +string DebugPrint(RestrictionCollector::Type const & type); +string DebugPrint(RestrictionCollector::Restriction const & restriction);