#include "routing/restriction_loader.hpp" #include "routing/restrictions_serialization.hpp" #include "routing/road_index.hpp" #include "indexer/mwm_set.hpp" #include "base/stl_helpers.hpp" #include #include #include #include #include namespace { using namespace routing; /// \returns if features |r1| and |r2| have a common end returns its joint id. /// If not, returns Joint::kInvalidId. /// \note It's possible that the both ends of |r1| and |r2| have common joint ids. /// In that case returns any of them. /// \note In general case ends of features don't have to be joints. For example all /// loose feature ends aren't joints. But if ends of r1 and r2 are connected at this /// point there has to be a joint. So the method is valid. Joint::Id GetCommonEndJoint(RoadJointIds const & r1, RoadJointIds const & r2) { auto const & j11 = r1.GetJointId(0 /* point id */); auto const & j12 = r1.GetEndingJointId(); auto const & j21 = r2.GetJointId(0 /* point id */); auto const & j22 = r2.GetEndingJointId(); if (j11 == j21 || j11 == j22) return j11; if (j12 == j21 || j12 == j22) return j12; return Joint::kInvalidId; } } // namespace namespace routing { RestrictionLoader::RestrictionLoader(MwmValue const & mwmValue, IndexGraph const & graph) : m_countryFileName(mwmValue.GetCountryFileName()) { if (!mwmValue.m_cont.IsExist(RESTRICTIONS_FILE_TAG)) return; try { m_reader = std::make_unique(mwmValue.m_cont.GetReader(RESTRICTIONS_FILE_TAG)); ReaderSource src(*m_reader); m_header.Deserialize(src); RestrictionVec restrictionsOnly; RestrictionSerializer::Deserialize(m_header, m_restrictions /* restriction No */, restrictionsOnly, src); ConvertRestrictionsOnlyToNoAndSort(graph, restrictionsOnly, m_restrictions); } catch (Reader::OpenException const & e) { m_header.Reset(); LOG(LERROR, ("File", m_countryFileName, "Error while reading", RESTRICTIONS_FILE_TAG, "section.", e.Msg())); throw; } } bool RestrictionLoader::HasRestrictions() const { return !m_restrictions.empty(); } RestrictionVec && RestrictionLoader::StealRestrictions() { return std::move(m_restrictions); } bool IsRestrictionFromRoads(IndexGraph const & graph, std::vector const & restriction) { for (auto const & featureId : restriction) { if (!graph.IsRoad(featureId)) return false; } return true; } /// \brief We store |Only| restriction in mwm, like /// Only, featureId_1, ... , featureId_N /// /// We convert such restriction to |No| following way: /// For each M, greater than 1, and M first features: /// featureId_1, ... , featureId_(M - 1), featureId_M, ... /// /// We create restrictionNo from features: /// featureId_1, ... , featureId_(M - 1), featureId_K /// where featureId_K - has common joint with featureId_(M - 1) and featureId_M. void ConvertRestrictionsOnlyToNoAndSort(IndexGraph const & graph, RestrictionVec const & restrictionsOnly, RestrictionVec & restrictionsNo) { for (std::vector const & restriction : restrictionsOnly) { CHECK_GREATER_OR_EQUAL(restriction.size(), 2, ()); if (!IsRestrictionFromRoads(graph, restriction)) continue; for (size_t i = 1; i < restriction.size(); ++i) { auto const curFeatureId = restriction[i]; auto const prevFeatureId = restriction[i - 1]; // Looking for a joint of an intersection of prev and cur features. Joint::Id const common = GetCommonEndJoint(graph.GetRoad(curFeatureId), graph.GetRoad(prevFeatureId)); if (common == Joint::kInvalidId) break; std::vector commonFeatures; commonFeatures.resize(i + 1); std::copy(restriction.begin(), restriction.begin() + i, commonFeatures.begin()); graph.ForEachPoint(common, [&](RoadPoint const & rp) { if (rp.GetFeatureId() != curFeatureId) { commonFeatures.back() = rp.GetFeatureId(); restrictionsNo.emplace_back(commonFeatures); } }); } } base::SortUnique(restrictionsNo); } } // namespace routing