#include "openlr/helpers.hpp" #include "openlr/road_info_getter.hpp" #include "routing/features_road_graph.hpp" #include "geometry/mercator.hpp" #include #include #include #include #include "boost/optional.hpp" namespace { using namespace openlr; using namespace std; openlr::FunctionalRoadClass HighwayClassToFunctionalRoadClass(ftypes::HighwayClass const & hwClass) { switch (hwClass) { case ftypes::HighwayClass::Trunk: return openlr::FunctionalRoadClass::FRC0; case ftypes::HighwayClass::Primary: return openlr::FunctionalRoadClass::FRC1; case ftypes::HighwayClass::Secondary: return openlr::FunctionalRoadClass::FRC2; case ftypes::HighwayClass::Tertiary: return openlr::FunctionalRoadClass::FRC3; case ftypes::HighwayClass::LivingStreet: return openlr::FunctionalRoadClass::FRC4; case ftypes::HighwayClass::Service: return openlr::FunctionalRoadClass::FRC5; default: return openlr::FunctionalRoadClass::FRC7; } } /// \returns boost::none if |e| doesn't conform to |functionalRoadClass| and score otherwise. boost::optional GetFrcScore(Graph::Edge const & e, FunctionalRoadClass functionalRoadClass, RoadInfoGetter & infoGetter) { CHECK(!e.IsFake(), ()); Score constexpr kMaxScoreForFrc = 25; if (functionalRoadClass == FunctionalRoadClass::NotAValue) return boost::none; auto const hwClass = infoGetter.Get(e.GetFeatureId()).m_hwClass; switch (functionalRoadClass) { case FunctionalRoadClass::FRC0: // Note. HighwayClass::Trunk means motorway, motorway_link, trunk or trunk_link. return hwClass == ftypes::HighwayClass::Trunk ? boost::optional(kMaxScoreForFrc) : boost::none; case FunctionalRoadClass::FRC1: return (hwClass == ftypes::HighwayClass::Trunk || hwClass == ftypes::HighwayClass::Primary) ? boost::optional(kMaxScoreForFrc) : boost::none; case FunctionalRoadClass::FRC2: case FunctionalRoadClass::FRC3: if (hwClass == ftypes::HighwayClass::Secondary || hwClass == ftypes::HighwayClass::Tertiary) return boost::optional(kMaxScoreForFrc); return hwClass == ftypes::HighwayClass::Primary || hwClass == ftypes::HighwayClass::LivingStreet ? boost::optional(0) : boost::none; case FunctionalRoadClass::FRC4: if (hwClass == ftypes::HighwayClass::LivingStreet || hwClass == ftypes::HighwayClass::Service) return boost::optional(kMaxScoreForFrc); return hwClass == ftypes::HighwayClass::Tertiary ? boost::optional(0) : boost::none; case FunctionalRoadClass::FRC5: case FunctionalRoadClass::FRC6: case FunctionalRoadClass::FRC7: return hwClass == ftypes::HighwayClass::LivingStreet || hwClass == ftypes::HighwayClass::Service ? boost::optional(kMaxScoreForFrc) : boost::none; case FunctionalRoadClass::NotAValue: UNREACHABLE(); } UNREACHABLE(); } } // namespace namespace openlr { // BearingPointsSelector --------------------------------------------------------------------------- BearingPointsSelector::BearingPointsSelector(uint32_t bearDistM, bool isLastPoint) : m_bearDistM(bearDistM), m_isLastPoint(isLastPoint) { } m2::PointD BearingPointsSelector::GetStartPoint(Graph::Edge const & e) const { return m_isLastPoint ? e.GetEndPoint() : e.GetStartPoint(); } m2::PointD BearingPointsSelector::GetEndPoint(Graph::Edge const & e, double distanceM) const { if (distanceM < m_bearDistM && m_bearDistM <= distanceM + EdgeLength(e)) { auto const edgeLen = EdgeLength(e); auto const edgeBearDist = min(m_bearDistM - distanceM, edgeLen); CHECK_LESS_OR_EQUAL(edgeBearDist, edgeLen, ()); return m_isLastPoint ? PointAtSegmentM(e.GetEndPoint(), e.GetStartPoint(), edgeBearDist) : PointAtSegmentM(e.GetStartPoint(), e.GetEndPoint(), edgeBearDist); } return m_isLastPoint ? e.GetStartPoint() : e.GetEndPoint(); } bool PointsAreClose(m2::PointD const & p1, m2::PointD const & p2) { double const kMwmRoadCrossingRadiusMeters = routing::GetRoadCrossingRadiusMeters(); return MercatorBounds::DistanceOnEarth(p1, p2) < kMwmRoadCrossingRadiusMeters; } double EdgeLength(Graph::Edge const & e) { return MercatorBounds::DistanceOnEarth(e.GetStartPoint(), e.GetEndPoint()); } bool EdgesAreAlmostEqual(Graph::Edge const & e1, Graph::Edge const & e2) { // TODO(mgsergio): Do I need to check fields other than points? return PointsAreClose(e1.GetStartPoint(), e2.GetStartPoint()) && PointsAreClose(e1.GetEndPoint(), e2.GetEndPoint()); } string LogAs2GisPath(Graph::EdgeVector const & path) { CHECK(!path.empty(), ("Paths should not be empty")); ostringstream ost; ost << "https://2gis.ru/moscow?queryState="; auto ll = MercatorBounds::ToLatLon(path.front().GetStartPoint()); ost << "center%2F" << ll.m_lon << "%2C" << ll.m_lat << "%2F"; ost << "zoom%2F" << 17 << "%2F"; ost << "ruler%2Fpoints%2F"; for (auto const & e : path) { ll = MercatorBounds::ToLatLon(e.GetStartPoint()); ost << ll.m_lon << "%20" << ll.m_lat << "%2C"; } ll = MercatorBounds::ToLatLon(path.back().GetEndPoint()); ost << ll.m_lon << "%20" << ll.m_lat; return ost.str(); } string LogAs2GisPath(Graph::Edge const & e) { return LogAs2GisPath(Graph::EdgeVector({e})); } bool PassesRestriction(Graph::Edge const & e, FunctionalRoadClass restriction, FormOfWay formOfWay, int frcThreshold, RoadInfoGetter & infoGetter) { if (e.IsFake() || restriction == FunctionalRoadClass::NotAValue) return true; auto const frc = HighwayClassToFunctionalRoadClass(infoGetter.Get(e.GetFeatureId()).m_hwClass); return static_cast(frc) <= static_cast(restriction) + frcThreshold; } bool PassesRestrictionV3(Graph::Edge const & e, FunctionalRoadClass functionalRoadClass, FormOfWay formOfWay, RoadInfoGetter & infoGetter, Score & score) { CHECK(!e.IsFake(), ("Edges should not be fake:", e)); auto const frcScore = GetFrcScore(e, functionalRoadClass, infoGetter); if (frcScore == boost::none) return false; score = frcScore.get(); Score constexpr kScoreForFormOfWay = 25; if (formOfWay == FormOfWay::Roundabout && infoGetter.Get(e.GetFeatureId()).m_isRoundabout) score += kScoreForFormOfWay; return true; } bool ConformLfrcnp(Graph::Edge const & e, FunctionalRoadClass lowestFrcToNextPoint, int frcThreshold, RoadInfoGetter & infoGetter) { if (e.IsFake() || lowestFrcToNextPoint == FunctionalRoadClass::NotAValue) return true; auto const frc = HighwayClassToFunctionalRoadClass(infoGetter.Get(e.GetFeatureId()).m_hwClass); return static_cast(frc) <= static_cast(lowestFrcToNextPoint) + frcThreshold; } bool ConformLfrcnpV3(Graph::Edge const & e, FunctionalRoadClass lowestFrcToNextPoint, RoadInfoGetter & infoGetter) { return GetFrcScore(e, lowestFrcToNextPoint, infoGetter) != boost::none; } size_t IntersectionLen(Graph::EdgeVector a, Graph::EdgeVector b) { sort(a.begin(), a.end()); sort(b.begin(), b.end()); return set_intersection(a.begin(), a.end(), b.begin(), b.end(), CounterIterator()).GetCount(); } bool SuffixEqualsPrefix(Graph::EdgeVector const & a, Graph::EdgeVector const & b, size_t len) { CHECK_LESS_OR_EQUAL(len, a.size(), ()); CHECK_LESS_OR_EQUAL(len, b.size(), ()); return equal(a.end() - len, a.end(), b.begin()); } // Returns a length of the longest suffix of |a| that matches any prefix of |b|. // Neither |a| nor |b| can contain several repetitions of any edge. // Returns -1 if |a| intersection |b| is not equal to some suffix of |a| and some prefix of |b|. int32_t PathOverlappingLen(Graph::EdgeVector const & a, Graph::EdgeVector const & b) { auto const len = IntersectionLen(a, b); if (SuffixEqualsPrefix(a, b, len)) return base::checked_cast(len); return -1; } m2::PointD PointAtSegmentM(m2::PointD const & p1, m2::PointD const & p2, double const distanceM) { auto const v = p2 - p1; auto const l = v.Length(); auto const L = MercatorBounds::DistanceOnEarth(p1, p2); auto const delta = distanceM * l / L; return PointAtSegment(p1, p2, delta); } } // namespace openlr