#pragma once #include "routing/base/astar_algorithm.hpp" #include "routing/base/astar_progress.hpp" #include "routing/base/routing_result.hpp" #include "routing/data_source.hpp" #include "routing/directions_engine.hpp" #include "routing/edge_estimator.hpp" #include "routing/fake_edges_container.hpp" #include "routing/features_road_graph.hpp" #include "routing/guides_connections.hpp" #include "routing/nearest_edge_finder.hpp" #include "routing/regions_decl.hpp" #include "routing/router.hpp" #include "routing/routing_callbacks.hpp" #include "routing/segment.hpp" #include "routing/segmented_route.hpp" #include "routing_common/num_mwm_id.hpp" #include "routing_common/vehicle_model.hpp" #include "platform/country_file.hpp" #include "geometry/point2d.hpp" #include "geometry/tree4d.hpp" #include #include #include #include #include namespace traffic { class TrafficCache; } namespace routing { class IndexGraph; class IndexGraphStarter; class IndexRouter : public IRouter { public: /** * @brief Indicates the mode in which the router is operating. * * The mode controls some aspects of router behavior, such as asking for additional maps or how * checkpoints are matched to nearby segments. */ enum Mode { /** * Router mode for navigation, i.e. user-initiated route guidance. */ Navigation, /** * Router mode for location decoding. */ Decoding }; class BestEdgeComparator final { public: BestEdgeComparator(m2::PointD const & point, m2::PointD const & direction); /// \returns -1 if |edge1| is closer to |m_point| and |m_direction| than |edge2|. /// returns 0 if |edge1| and |edge2| have almost the same direction and are equidistant from |m_point|. /// returns 1 if |edge1| is further from |m_point| and |m_direction| than |edge2|. int Compare(Edge const & edge1, Edge const & edge2) const; bool IsDirectionValid() const { return !m_direction.IsAlmostZero(); } /// \brief According to current implementation vectors |edge| and |m_direction| /// are almost collinear and co-directional if the angle between them is less than 14 degrees. bool IsAlmostCodirectional(Edge const & edge) const; /// \returns the square of shortest distance from |m_point| to |edge| in mercator. double GetSquaredDist(Edge const & edge) const; private: m2::PointD const m_point; m2::PointD const m_direction; }; /** * @brief Creates a new `IndexRouter` instance. * * This is the constructor intended for normal routing. It requires a `TrafficCache` argument, * from which it may create a traffic stash so the traffic situation can be considered for the * route, depending on the vehicle type. * * @param vehicleType The vehichle type * @param loadAltitudes Whether to load altitudes * @param countryParentNameGetterFn Function which converts a country name into the name of its parent country) * @param countryFileFn Function which converts a pointer to its country name * @param countryRectFn Function which returns the rect for a country * @param numMwmIds MWMs to use for route calculation (this should include all MWMs, whether or * not we have the file locally, but not World or WorldCoasts) * @param numMwmTree * @param trafficCache The traffic cache (used only if `vehicleType` is `VehicleType::Car`) * @param dataSource The MWM data source */ IndexRouter(VehicleType vehicleType, bool loadAltitudes, CountryParentNameGetterFn const & countryParentNameGetterFn, TCountryFileFn const & countryFileFn, CountryRectFn const & countryRectFn, std::shared_ptr numMwmIds, std::unique_ptr> numMwmTree, traffic::TrafficCache const & trafficCache, DataSource & dataSource); std::unique_ptr MakeSingleMwmWorldGraph(); // IRouter overrides: std::string GetName() const override { return m_name; } void ClearState() override; void SetGuides(GuidesTracks && guides) override; RouterResultCode CalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, bool adjustToPrevRoute, RouterDelegate const & delegate, Route & route) override; bool FindClosestProjectionToRoad(m2::PointD const & point, m2::PointD const & direction, double radius, EdgeProj & proj) override; bool GetBestOutgoingEdges(m2::PointD const & checkpoint, WorldGraph & graph, std::vector & edges); VehicleType GetVehicleType() const { return m_vehicleType; } protected: /** * @brief Creates a new `IndexRouter` instance. * * This constructor is intended for use by the TraFF decoder, not for normal routing. It differs * from the general-purpose constructor in two ways. * * It takes an explicit `EdgeEstimator` argument, instance, which gives the caller fine-grained * control over the cost calculations used for routing by supplying an `EdgeEstimator` of their * choice. * * It also lacks the `TrafficCache` argument and never creates a traffic stash. This creates a * router instance which ignores the traffic situation, regardless of the vehicle type. * * @param vehicleType The vehichle type * @param loadAltitudes Whether to load altitudes * @param countryParentNameGetterFn Function which converts a country name into the name of its parent country) * @param countryFileFn Function which converts a pointer to its country name * @param countryRectFn Function which returns the rect for a country * @param numMwmIds * @param numMwmTree * @param estimator An edge estimator * @param dataSource The MWM data source */ IndexRouter(VehicleType vehicleType, bool loadAltitudes, CountryParentNameGetterFn const & countryParentNameGetterFn, TCountryFileFn const & countryFileFn, CountryRectFn const & countryRectFn, std::shared_ptr numMwmIds, std::unique_ptr> numMwmTree, std::shared_ptr estimator, DataSource & dataSource); /** * @brief Returns the mode in which the router is operating. * * The `IndexRouter` always returns `Mode::Navigation`; subclasses may override this method and * return different values. * * In navigation mode, the router may exit with `RouterResultCode::NeedMoreMaps` if it determines * that a better route can be calculated with additional maps. When snapping endpoints to edges, * it will consider only edges which are not “fenced off” by other edges, i.e. which can be * reached from the endpoint without crossing other edges. This decreases the number of fake * endings and thus speeds up routing, without any undesirable side effects for that use case. * * In decoding mode, the router will never exit with `RouterResultCode::NeedMoreMaps`: it will try * to find a route with the existing maps, or exit without finding a route. When snapping * endpoints to edges, it considers all edges within the given radius, fenced off or not. */ virtual Mode GetMode() { return Mode::Navigation; } /** * @brief Returns current routing options. * * In this class, the routing options are the one set in the GUI. Subclasses may override this * method to provide different routing options. */ virtual RoutingOptions GetRoutingOptions(); private: RouterResultCode CalculateSubrouteJointsMode(IndexGraphStarter & starter, RouterDelegate const & delegate, std::shared_ptr const & progress, std::vector & subroute); RouterResultCode CalculateSubrouteNoLeapsMode(IndexGraphStarter & starter, RouterDelegate const & delegate, std::shared_ptr const & progress, std::vector & subroute); RouterResultCode CalculateSubrouteLeapsOnlyMode(Checkpoints const & checkpoints, size_t subrouteIdx, IndexGraphStarter & starter, RouterDelegate const & delegate, std::shared_ptr const & progress, std::vector & subroute); RouterResultCode DoCalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, RouterDelegate const & delegate, Route & route); RouterResultCode CalculateSubroute(Checkpoints const & checkpoints, size_t subrouteIdx, RouterDelegate const & delegate, std::shared_ptr const & progress, IndexGraphStarter & graph, std::vector & subroute, bool guidesActive = false); RouterResultCode AdjustRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, RouterDelegate const & delegate, Route & route); std::unique_ptr MakeWorldGraph(); using EdgeProjectionT = IRoadGraph::EdgeProjectionT; class PointsOnEdgesSnapping { IndexRouter & m_router; WorldGraph & m_graph; using RoadInfoT = IRoadGraph::FullRoadInfo; using EdgeProjectionT = IndexRouter::EdgeProjectionT; // The idea here is to store dead-ends from the successful previous call, not to filter // dead-end candidates if they belong to one graph's cluster (island). std::set m_deadEnds[2]; std::vector m_startSegments; static uint32_t constexpr kFirstSearchDistanceM = 40; public: PointsOnEdgesSnapping(IndexRouter & router, WorldGraph & graph) : m_router(router), m_graph(graph) {} /// @return 0 - ok, 1 - start not found, 2 - finish not found. int Snap(m2::PointD const & start, m2::PointD const & finish, m2::PointD const & direction, FakeEnding & startEnding, FakeEnding & finishEnding, bool & startIsCodirectional); void SetNextStartSegment(Segment const & seg) { m_startSegments = {seg}; } private: void FillDeadEndsCache(m2::PointD const & point); /// \brief Removes all roads from |roads| that go to dead ends and all roads that /// are not good according to |worldGraph|. For car routing there are roads with hwtag nocar as well. /// \param checkpoint which is used to look for the closest segment in a road. The closest segment /// is used then to check if it's a dead end. void EraseIfDeadEnd(m2::PointD const & checkpoint, std::vector & roads, std::set & deadEnds) const; /// \returns true if a segment (|point|, |edgeProjection.second|) crosses one of segments /// in |fences| except for the one which has the same geometry with |edgeProjection.first|. static bool IsFencedOff(m2::PointD const & point, EdgeProjectionT const & edgeProjection, std::vector const & fences); void RoadsToNearestEdges(m2::PointD const & point, std::vector const & roads, IsEdgeProjGood const & isGood, std::vector & edgeProj); Segment GetSegmentByEdge(Edge const & edge) const; public: /// \brief Fills |closestCodirectionalEdge| with a codirectional edge which is closest to /// |point| and returns true if there's any. If not returns false. static bool FindClosestCodirectionalEdge(m2::PointD const & point, m2::PointD const & direction, std::vector const & candidates, Edge & closestCodirectionalEdge); /// \brief Finds best segments (edges) that may be considered as starts or finishes /// of the route. According to current implementation the closest to |checkpoint| segment which /// is almost codirectianal to |direction| is the best. /// If there's no an almost codirectional segment in the neighbourhood then all not dead end /// candidates that may be reached without crossing the road graph will be added to |bestSegments|. /// \param isOutgoing == true if |checkpoint| is considered as the start of the route. /// isOutgoing == false if |checkpoint| is considered as the finish of the route. /// \param bestSegmentIsAlmostCodirectional is filled with true if |bestSegment| is chosen /// because |direction| and direction of |bestSegment| are almost equal and with false otherwise. /// \return true if the best segment is found and false otherwise. /// \note Candidates in |bestSegments| are sorted from better to worse. bool FindBestSegments(m2::PointD const & checkpoint, m2::PointD const & direction, bool isOutgoing, std::vector & bestSegments, bool & bestSegmentIsAlmostCodirectional); bool FindBestEdges(m2::PointD const & checkpoint, m2::PointD const & direction, bool isOutgoing, double closestEdgesRadiusM, std::vector & bestEdges, bool & bestSegmentIsAlmostCodirectional); }; using RoutingResultT = RoutingResult; class RoutesCalculator { std::map, RoutingResultT> m_cache; IndexGraphStarter & m_starter; RouterDelegate const & m_delegate; public: RoutesCalculator(IndexGraphStarter & starter, RouterDelegate const & delegate) : m_starter(starter) , m_delegate(delegate) {} using ProgressPtrT = std::shared_ptr; RoutingResultT const * Calc(Segment const & beg, Segment const & end, ProgressPtrT const & progress, double progressCoef); // Makes JointSingleMwm first and Joints then, if first attempt was failed. RoutingResultT const * Calc2Times(Segment const & beg, Segment const & end, ProgressPtrT const & progress, double progressCoef); }; // Input route may contains 'leaps': shortcut edges from mwm border enter to exit. // ProcessLeaps replaces each leap with calculated route through mwm. RouterResultCode ProcessLeapsJoints(std::vector const & input, IndexGraphStarter & starter, std::shared_ptr const & progress, RoutesCalculator & calculator, RoutingResultT & result); RouterResultCode RedressRoute(std::vector const & segments, base::Cancellable const & cancellable, IndexGraphStarter & starter, Route & route); bool AreSpeedCamerasProhibited(NumMwmId mwmID) const; bool AreMwmsNear(IndexGraphStarter const & starter) const; bool DoesTransitSectionExist(NumMwmId numMwmId); RouterResultCode ConvertTransitResult(std::set const & mwmIds, RouterResultCode resultCode); /// \brief Fills |speedcamProhibitedMwms| with mwms which are crossed by |segments| /// where speed cameras are prohibited. void FillSpeedCamProhibitedMwms(std::vector const & segments, std::vector & speedCamProhibitedMwms) const; template RouterResultCode ConvertResult(typename AStarAlgorithm::Result result) const { switch (result) { case AStarAlgorithm::Result::NoPath: return RouterResultCode::RouteNotFound; case AStarAlgorithm::Result::Cancelled: return RouterResultCode::Cancelled; case AStarAlgorithm::Result::OK: return RouterResultCode::NoError; } UNREACHABLE(); } template RouterResultCode FindPath(AStarParams & params, std::set const & mwmIds, RoutingResult & routingResult) { AStarAlgorithm algorithm; return ConvertTransitResult( mwmIds, ConvertResult(algorithm.FindPathBidirectional(params, routingResult))); } void SetupAlgorithmMode(IndexGraphStarter & starter, bool guidesActive = false) const; uint32_t ConnectTracksOnGuidesToOsm(std::vector const & checkpoints, WorldGraph & graph); void ConnectCheckpointsOnGuidesToOsm(std::vector const & checkpoints, WorldGraph & graph); void AddGuidesOsmConnectionsToGraphStarter(size_t checkpointIdxFrom, size_t checkpointIdxTo, IndexGraphStarter & starter); void AppendPartsOfReal(LatLonWithAltitude const & point1, LatLonWithAltitude const & point2, uint32_t & startIdx, ConnectionToOsm & link); std::vector GetBestOutgoingSegments(m2::PointD const & checkpoint, WorldGraph & graph); VehicleType m_vehicleType; bool m_loadAltitudes; std::string const m_name; MwmDataSource m_dataSource; std::shared_ptr m_vehicleModelFactory; TCountryFileFn const m_countryFileFn; CountryRectFn const m_countryRectFn; std::shared_ptr m_numMwmIds; std::shared_ptr> m_numMwmTree; std::shared_ptr m_trafficStash; FeaturesRoadGraphBase m_roadGraph; std::shared_ptr m_estimator; std::unique_ptr m_directionsEngine; std::unique_ptr m_lastRoute; std::unique_ptr m_lastFakeEdges; // If a ckeckpoint is near to the guide track we need to build route through this track. GuidesConnections m_guides; CountryParentNameGetterFn m_countryParentNameGetterFn; }; } // namespace routing