diff --git a/generator/transit_generator.cpp b/generator/transit_generator.cpp index 439f3583db..be6ffbfa67 100644 --- a/generator/transit_generator.cpp +++ b/generator/transit_generator.cpp @@ -2,13 +2,27 @@ #include "generator/osm_id.hpp" +#include "traffic/traffic_cache.hpp" + +#include "routing/index_router.hpp" + #include "routing_common/transit_serdes.hpp" #include "routing_common/transit_types.hpp" +#include "storage/country_info_getter.hpp" +#include "storage/routing_helpers.hpp" + +#include "indexer/index.hpp" + +#include "geometry/point2d.hpp" + #include "coding/file_container.hpp" #include "coding/file_name_utils.hpp" #include "coding/file_writer.hpp" +#include "platform/country_file.hpp" +#include "platform/local_country_file.hpp" +#include "platform/local_country_file_utils.hpp" #include "platform/platform.hpp" #include "base/checked_cast.hpp" @@ -16,6 +30,8 @@ #include "base/macros.hpp" #include +#include +#include #include "3party/jansson/src/jansson.h" @@ -43,20 +59,82 @@ string GetFileName(string const & filePath) return name; } -/// \brief Reads from |root| (json) and serializes an array to |serializer|. -/// \param handler is function which fixes up vector of |Item|(s) after deserialization from json -/// but before serialization to mwm. template -void SerializeObject(my::Json const & root, string const & key, Serializer & serializer, - function &)> handler = nullptr) +void DeserializeFromJson(my::Json const & root, string const & key, vector & items) { - vector items; - + items.clear(); DeserializerFromJson deserializer(root.get()); deserializer(items, key.c_str()); +} - if (handler) - handler(items); +void DeserializerGatesFromJson(my::Json const & root, string const & mwmDir, string const & countryId, + vector & gates) +{ + DeserializeFromJson(root, "gates", gates); + + // Creating IndexRouter. + CountryFile const countryFile(countryId); + LocalCountryFile localCountryFile(mwmDir, countryFile, 0 /* version */); + localCountryFile.SyncWithDisk(); + CHECK_EQUAL(localCountryFile.GetFiles(), MapOptions::MapWithCarRouting, + ("No correct mwm corresponding local country file:", localCountryFile, + ". Path:", my::JoinFoldersToPath(mwmDir, countryId + DATA_FILE_EXTENSION))); + + Index index; + pair const result = index.Register(localCountryFile); + CHECK_EQUAL(result.second, MwmSet::RegResult::Success, ()); + + Platform platform; + unique_ptr infoGetter = + storage::CountryInfoReader::CreateCountryInfoReader(platform); + CHECK(infoGetter, ()); + + auto const countryFileGetter = [&infoGetter](m2::PointD const & pt) { + return infoGetter->GetRegionCountryId(pt); + }; + + auto const getMwmRectByName = [&](string const & c) -> m2::RectD { + CHECK_EQUAL(countryId, c, ()); + return infoGetter->GetLimitRectForLeaf(c); + }; + + auto const mwmId = index.GetMwmIdByCountryFile(countryFile); + CHECK_EQUAL(mwmId.GetInfo()->GetType(), MwmInfo::COUNTRY, ()); + auto numMwmIds = make_shared(); + numMwmIds->RegisterFile(countryFile); + + // Note. |indexRouter| is valid until |index| is valid. + IndexRouter indexRouter(VehicleType::Pedestrian, false /* load altitudes */, + CountryParentNameGetterFn(), countryFileGetter, getMwmRectByName, + numMwmIds, MakeNumMwmTree(*numMwmIds, *infoGetter), + traffic::TrafficCache(), index); + unique_ptr worldGraph = indexRouter.MakeWorldGraph(); + worldGraph->SetMode(WorldGraph::Mode::SingleMwm); + + // Looking for the best segment for every gate. + bool dummy; + for (Gate & gate : gates) + { + // Note. For pedestrian routing all the segments are considered as twoway segments so + // IndexRouter.FindBestSegment() method finds the same segment for |isOutgoing| == true + // and |isOutgoing| == false. + Segment bestSegment; + if (indexRouter.FindBestSegment(gate.GetPoint(), m2::PointD::Zero() /* direction */, + true /* isOutgoing */, *worldGraph, bestSegment, dummy)) + { + // Note. |numMwmIds| with only one mwm is used to create |indexRouter|. So |Segment::m_mwmId| + // is not valid at |bestSegment| and is not copied to |Gate::m_bestPedestrianSegment|. + gate.SetBestPedestrianSegment(bestSegment); + } + } +} + +/// \brief Reads from |root| (json) and serializes an array to |serializer|. +template +void SerializeObject(my::Json const & root, string const & key, Serializer & serializer) +{ + vector items; + DeserializeFromJson(root, key, items); serializer(items); } @@ -122,6 +200,11 @@ void BuildTransit(string const & mwmPath, string const & transitDir) my::Json root(jsonBuffer.c_str()); CHECK(root.get() != nullptr, ("Cannot parse the json file:", graphFullPath)); + // Note. |gates| has to be deserialized from json before to start writing transit section to mwm since + // the mwm is used to filled |gates|. + vector gates; + DeserializerGatesFromJson(root, my::GetDirectory(mwmPath), countryId, gates); + FilesContainerW cont(mwmPath, FileWriter::OP_WRITE_EXISTING); FileWriter w = cont.GetWriter(TRANSIT_FILE_TAG); @@ -134,12 +217,7 @@ void BuildTransit(string const & mwmPath, string const & transitDir) SerializeObject(root, "stops", serializer); header.m_gatesOffset = base::checked_cast(w.Pos() - startOffset); - auto const fillPedestrianFeatureIds = [](vector & gates) - { - // @TODO(bykoianko) |m_pedestrianFeatureIds| is not filled from json but should be calculated based on |m_point|. - }; - - SerializeObject(root, "gates", serializer, fillPedestrianFeatureIds); + serializer(gates); header.m_edgesOffset = base::checked_cast(w.Pos() - startOffset); SerializeObject(root, "edges", serializer); diff --git a/generator/transit_generator.hpp b/generator/transit_generator.hpp index 8a6fd99c7e..bb9c035e42 100644 --- a/generator/transit_generator.hpp +++ b/generator/transit_generator.hpp @@ -83,6 +83,10 @@ private: /// \param mwmPath relative or full path to an mwm. The name of mwm without extension is considered /// as country id. /// \param transitDir a path to directory with json files with transit graphs. +/// \note An mwm pointed by |mwmPath| should contain: +/// * section which is necessary to register it at Index +/// * index graph (ROUTING_FILE_TAG) and restriction sections (RESTRICTIONS_FILE_TAG) +/// This information will be used in this method. void BuildTransit(std::string const & mwmPath, std::string const & transitDir); } // namespace transit } // namespace routing diff --git a/routing/index_router.cpp b/routing/index_router.cpp index d1d16f21b4..f8fd3b1ab0 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -375,6 +375,59 @@ IRouter::ResultCode IndexRouter::CalculateRoute(Checkpoints const & checkpoints, } } +bool IndexRouter::FindBestSegment(m2::PointD const & point, m2::PointD const & direction, + bool isOutgoing, WorldGraph & worldGraph, Segment & bestSegment, + bool & bestSegmentIsAlmostCodirectional) const +{ + auto const file = platform::CountryFile(m_countryFileFn(point)); + MwmSet::MwmHandle handle = m_index.GetMwmHandleByCountryFile(file); + if (!handle.IsAlive()) + MYTHROW(RoutingException, ("Can't get mwm handle for", file)); + + auto const mwmId = MwmSet::MwmId(handle.GetInfo()); + NumMwmId const numMwmId = m_numMwmIds->GetId(file); + + vector> candidates; + m_roadGraph.FindClosestEdges(point, kMaxRoadCandidates, candidates); + + auto const getSegmentByEdge = [&numMwmId](Edge const & edge) { + return Segment(numMwmId, edge.GetFeatureId().m_index, edge.GetSegId(), edge.IsForward()); + }; + + // Getting rid of knowingly bad candidates. + my::EraseIf(candidates, [&](pair const & p){ + Edge const & edge = p.first; + return edge.GetFeatureId().m_mwmId != mwmId || IsDeadEnd(getSegmentByEdge(edge), isOutgoing, worldGraph); + }); + + if (candidates.empty()) + return false; + + BestEdgeComparator bestEdgeComparator(point, direction); + Edge bestEdge = candidates[0].first; + for (size_t i = 1; i < candidates.size(); ++i) + { + Edge const & edge = candidates[i].first; + if (bestEdgeComparator.Compare(edge, bestEdge) < 0) + bestEdge = edge; + } + + bestSegmentIsAlmostCodirectional = + bestEdgeComparator.IsDirectionValid() && bestEdgeComparator.IsAlmostCodirectional(bestEdge); + bestSegment = getSegmentByEdge(bestEdge); + return true; +} + +unique_ptr IndexRouter::MakeWorldGraph() +{ + return make_unique( + make_unique(m_numMwmIds, m_numMwmTree, m_vehicleModelFactory, m_vehicleType, + m_countryRectFn, m_index, m_indexManager), + IndexGraphLoader::Create(m_vehicleType, m_loadAltitudes, m_numMwmIds, m_vehicleModelFactory, + m_estimator, m_index), + m_estimator); +} + IRouter::ResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, RouterDelegate const & delegate, Route & route) @@ -656,59 +709,6 @@ IRouter::ResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, return IRouter::NoError; } -unique_ptr IndexRouter::MakeWorldGraph() -{ - return make_unique( - make_unique(m_numMwmIds, m_numMwmTree, m_vehicleModelFactory, m_vehicleType, - m_countryRectFn, m_index, m_indexManager), - IndexGraphLoader::Create(m_vehicleType, m_loadAltitudes, m_numMwmIds, m_vehicleModelFactory, - m_estimator, m_index), - m_estimator); -} - -bool IndexRouter::FindBestSegment(m2::PointD const & point, m2::PointD const & direction, - bool isOutgoing, WorldGraph & worldGraph, Segment & bestSegment, - bool & bestSegmentIsAlmostCodirectional) const -{ - auto const file = platform::CountryFile(m_countryFileFn(point)); - MwmSet::MwmHandle handle = m_index.GetMwmHandleByCountryFile(file); - if (!handle.IsAlive()) - MYTHROW(RoutingException, ("Can't get mwm handle for", file)); - - auto const mwmId = MwmSet::MwmId(handle.GetInfo()); - NumMwmId const numMwmId = m_numMwmIds->GetId(file); - - vector> candidates; - m_roadGraph.FindClosestEdges(point, kMaxRoadCandidates, candidates); - - auto const getSegmentByEdge = [&numMwmId](Edge const & edge) { - return Segment(numMwmId, edge.GetFeatureId().m_index, edge.GetSegId(), edge.IsForward()); - }; - - // Getting rid of knowingly bad candidates. - my::EraseIf(candidates, [&](pair const & p){ - Edge const & edge = p.first; - return edge.GetFeatureId().m_mwmId != mwmId || IsDeadEnd(getSegmentByEdge(edge), isOutgoing, worldGraph); - }); - - if (candidates.empty()) - return false; - - BestEdgeComparator bestEdgeComparator(point, direction); - Edge bestEdge = candidates[0].first; - for (size_t i = 1; i < candidates.size(); ++i) - { - Edge const & edge = candidates[i].first; - if (bestEdgeComparator.Compare(edge, bestEdge) < 0) - bestEdge = edge; - } - - bestSegmentIsAlmostCodirectional = - bestEdgeComparator.IsDirectionValid() && bestEdgeComparator.IsAlmostCodirectional(bestEdge); - bestSegment = getSegmentByEdge(bestEdge); - return true; -} - IRouter::ResultCode IndexRouter::ProcessLeaps(vector const & input, RouterDelegate const & delegate, WorldGraph::Mode prevMode, diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 368ac2ce30..2ca6a46786 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -69,6 +69,19 @@ public: bool adjustToPrevRoute, RouterDelegate const & delegate, Route & route) override; + /// \brief Finds the best segment (edge) which may be considered as the start of the finish of the route. + /// According to current implementation if a segment is near |point| and is almost codirectional + /// to |direction| vector, the segment will be better than others. If there's no an an almost codirectional + /// segment in neighbourhoods the closest segment to |point| will be chosen. + /// \param isOutgoing == true is |point| is considered as the start of the route. + /// isOutgoing == false is |point| is considered as the finish of the route. + /// \param bestSegmentIsAlmostCodirectional is filled with true if |bestSegment| is chosen + /// because vector |direction| and vector of |bestSegment| are almost equal and with false otherwise. + bool FindBestSegment(m2::PointD const & point, m2::PointD const & direction, bool isOutgoing, + WorldGraph & worldGraph, Segment & bestSegment, + bool & bestSegmentIsAlmostCodirectional) const; + std::unique_ptr MakeWorldGraph(); + private: IRouter::ResultCode DoCalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, @@ -82,19 +95,6 @@ private: m2::PointD const & startDirection, RouterDelegate const & delegate, Route & route); - std::unique_ptr MakeWorldGraph(); - - /// \brief Finds the best segment (edge) which may be considered as the start of the finish of the route. - /// According to current implementation if a segment is near |point| and is almost codirectional - /// to |direction| vector, the segment will be better than others. If there's no an an almost codirectional - /// segment in neighbourhoods the closest segment to |point| will be chosen. - /// \param isOutgoing == true is |point| is considered as the start of the route. - /// isOutgoing == false is |point| is considered as the finish of the route. - /// \param bestSegmentIsAlmostCodirectional is filled with true if |bestSegment| is chosen - /// because vector |direction| and vector of |bestSegment| are almost equal and with false otherwise. - bool FindBestSegment(m2::PointD const & point, m2::PointD const & direction, bool isOutgoing, - WorldGraph & worldGraph, Segment & bestSegment, - bool & bestSegmentIsAlmostCodirectional) const; // Input route may contains 'leaps': shortcut edges from mwm border enter to exit. // ProcessLeaps replaces each leap with calculated route through mwm. IRouter::ResultCode ProcessLeaps(std::vector const & input, diff --git a/routing_common/transit_types.cpp b/routing_common/transit_types.cpp index 7b2014daa6..b3bc697013 100644 --- a/routing_common/transit_types.cpp +++ b/routing_common/transit_types.cpp @@ -68,6 +68,14 @@ bool Stop::IsEqualForTesting(Stop const & stop) const m_lineIds == stop.m_lineIds && my::AlmostEqualAbs(m_point, stop.m_point, kPointsEqualEpsilon); } +// SingleMwmSegment ------------------------------------------------------------------------------- +SingleMwmSegment::SingleMwmSegment(Segment const & segment) + : m_featureId(segment.GetFeatureId()) + , m_segmentIdx(segment.GetSegmentIdx()) + , m_forward(segment.IsForward()) +{ +} + // Gate ------------------------------------------------------------------------------------------- Gate::Gate(FeatureId featureId, bool entrance, bool exit, double weight, std::vector const & stopIds, m2::PointD const & point) diff --git a/routing_common/transit_types.hpp b/routing_common/transit_types.hpp index fc9eb6a54d..5f958c0bc3 100644 --- a/routing_common/transit_types.hpp +++ b/routing_common/transit_types.hpp @@ -1,5 +1,7 @@ #pragma once +#include "routing/segment.hpp" + #include "geometry/point2d.hpp" #include "base/visitor.hpp" @@ -87,6 +89,26 @@ private: // and deserialization. }; +class SingleMwmSegment +{ +public: + SingleMwmSegment() = default; + SingleMwmSegment(Segment const & segment); + + FeatureId GetFeatureId() const { return m_featureId; } + uint32_t GetSegmentIdx() const { return m_segmentIdx; } + bool GetForward() const { return m_forward; } + + DECLARE_VISITOR_AND_DEBUG_PRINT(SingleMwmSegment, visitor(m_featureId, "feature_id"), + visitor(m_segmentIdx, "segment_idx"), + visitor(m_forward, "forward")) + + private: + FeatureId m_featureId = kInvalidFeatureId; + uint32_t m_segmentIdx = 0; + bool m_forward = false; +}; + class Gate { public: @@ -94,9 +116,10 @@ public: Gate(FeatureId featureId, bool entrance, bool exit, double weight, std::vector const & stopIds, m2::PointD const & point); bool IsEqualForTesting(Gate const & gate) const; + void SetBestPedestrianSegment(Segment const & s) { m_bestPedestrianSegment = SingleMwmSegment(s); } FeatureId GetFeatureId() const { return m_featureId; } - std::vector const & GetPedestrianFeatureIds() const { return m_pedestrianFeatureIds; } + SingleMwmSegment const & GetBestPedestrianSegment() const { return m_bestPedestrianSegment; } bool GetEntrance() const { return m_entrance; } bool GetExit() const { return m_exit; } double GetWeight() const { return m_weight; } @@ -104,7 +127,7 @@ public: m2::PointD const & GetPoint() const { return m_point; } DECLARE_VISITOR_AND_DEBUG_PRINT(Gate, visitor(m_featureId, "osm_id"), - visitor(m_pedestrianFeatureIds, "" /* name */), + visitor(m_bestPedestrianSegment, "best_pedestrian_segment"), visitor(m_entrance, "entrance"), visitor(m_exit, "exit"), visitor(m_weight, "weight"), visitor(m_stopIds, "stop_ids"), visitor(m_point, "point")) @@ -112,9 +135,8 @@ public: private: // |m_featureId| is feature id of a point feature which represents gates. FeatureId m_featureId = kInvalidFeatureId; - // |m_pedestrianFeatureIds| is linear feature ids which can be used for pedestrian routing - // to leave (to enter) the gate. - std::vector m_pedestrianFeatureIds; + // |m_bestPedestrianSegment| is a segment which can be used for pedestrian routing to leave an to enter the gate. + SingleMwmSegment m_bestPedestrianSegment; bool m_entrance = true; bool m_exit = true; double m_weight = kInvalidWeight;