diff options
37 files changed, 1530 insertions, 106 deletions
diff --git a/android/src/com/mapswithme/maps/routing/NavigationController.java b/android/src/com/mapswithme/maps/routing/NavigationController.java index dc42b35ef2..4e43bf7b98 100644 --- a/android/src/com/mapswithme/maps/routing/NavigationController.java +++ b/android/src/com/mapswithme/maps/routing/NavigationController.java @@ -30,7 +30,9 @@ import com.mapswithme.util.statistics.AlohaHelper; import com.mapswithme.util.statistics.Statistics; import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Calendar; +import java.util.Locale; import java.util.concurrent.TimeUnit; public class NavigationController implements TrafficManager.TrafficCallback @@ -281,8 +283,11 @@ public class NavigationController implements TrafficManager.TrafficCallback { final Calendar currentTime = Calendar.getInstance(); currentTime.add(Calendar.SECOND, seconds); - UiUtils.setTextAndShow(mTimeMinuteValue, DateFormat.getTimeInstance(DateFormat.SHORT) - .format(currentTime.getTime())); + final DateFormat timeFormat12 = new SimpleDateFormat("h:mm aa", Locale.getDefault()); + final DateFormat timeFormat24 = new SimpleDateFormat("HH:mm", Locale.getDefault()); + boolean is24Format = android.text.format.DateFormat.is24HourFormat(mTimeMinuteValue.getContext()); + UiUtils.setTextAndShow(mTimeMinuteValue, is24Format ? timeFormat24.format(currentTime.getTime()) + : timeFormat12.format(currentTime.getTime())); UiUtils.hide(mTimeHourUnits, mTimeHourValue, mTimeMinuteUnits); } diff --git a/coding/file_name_utils.hpp b/coding/file_name_utils.hpp index 1e3ffe83d1..4bcb32f616 100644 --- a/coding/file_name_utils.hpp +++ b/coding/file_name_utils.hpp @@ -3,6 +3,8 @@ #include "std/initializer_list.hpp" #include "std/string.hpp" +#include <utility> + namespace my { /// Remove extension from file name. @@ -22,10 +24,19 @@ namespace my /// Get folder separator for specific platform string GetNativeSeparator(); - /// Create full path from some folder using native folders separator + /// @deprecated use JoinPath instead. string JoinFoldersToPath(const string & folder, const string & file); string JoinFoldersToPath(initializer_list<string> const & folders, const string & file); /// Add the terminating slash to the folder path string if it's not already there. string AddSlashIfNeeded(string const & path); + + inline std::string JoinPath(std::string const & file) { return file; } + + /// Create full path from some folder using native folders separator. + template<typename... Args> + std::string JoinPath(std::string const & folder, Args&&... args) + { + return AddSlashIfNeeded(folder) + JoinPath(std::forward<Args>(args)...); + } } diff --git a/data/countries_meta.txt b/data/countries_meta.txt index d3020f5e9c..fa88d2ec00 100644 --- a/data/countries_meta.txt +++ b/data/countries_meta.txt @@ -546,6 +546,9 @@ "Taiwan": { "languages": ["zh"] }, +"Panama": { + "languages": ["es"] +}, "Peru": { "languages": ["es"] }, diff --git a/defines.hpp b/defines.hpp index ea9796a2b4..c8d87a2e8f 100644 --- a/defines.hpp +++ b/defines.hpp @@ -29,6 +29,7 @@ #define ALTITUDES_FILE_TAG "altitudes" #define RESTRICTIONS_FILE_TAG "restrictions" #define ROUTING_FILE_TAG "routing" +#define CROSS_MWM_FILE_TAG "cross_mwm" #define FEATURE_OFFSETS_FILE_TAG "offs" #define RANKS_FILE_TAG "ranks" #define REGION_INFO_FILE_TAG "rgninfo" diff --git a/generator/generator_tool/generator_tool.cpp b/generator/generator_tool/generator_tool.cpp index 151b9d5655..3cbcd60940 100644 --- a/generator/generator_tool/generator_tool.cpp +++ b/generator/generator_tool/generator_tool.cpp @@ -69,8 +69,9 @@ DEFINE_bool(split_by_polygons, false, // Routing. DEFINE_string(osrm_file_name, "", "Input osrm file to generate routing info."); DEFINE_bool(make_routing, false, "Make routing info based on osrm file."); -DEFINE_bool(make_cross_section, false, "Make cross section in routing file for cross mwm routing."); +DEFINE_bool(make_cross_section, false, "Make cross section in routing file for cross mwm routing (for OSRM routing)."); DEFINE_bool(make_routing_index, false, "Make sections with the routing information."); +DEFINE_bool(make_cross_mwm, false, "Make section for cross mwm routing (for dynamic indexed routing)."); DEFINE_string(srtm_path, "", "Path to srtm directory. If set, generates a section with altitude information " "about roads."); @@ -161,7 +162,7 @@ int main(int argc, char ** argv) FLAGS_generate_index || FLAGS_generate_search_index || FLAGS_calc_statistics || FLAGS_type_statistics || FLAGS_dump_types || FLAGS_dump_prefixes || FLAGS_dump_feature_names != "" || FLAGS_check_mwm || FLAGS_srtm_path != "" || - FLAGS_make_routing_index || FLAGS_generate_traffic_keys) + FLAGS_make_routing_index || FLAGS_make_cross_mwm || FLAGS_generate_traffic_keys) { classificator::Load(); classif().SortClassificator(); @@ -256,6 +257,9 @@ int main(int argc, char ** argv) routing::BuildRoutingIndex(datFile, country); } + if (FLAGS_make_cross_mwm) + routing::BuildCrossMwmSection(path, datFile, country); + if (FLAGS_generate_traffic_keys) { if (!traffic::GenerateTrafficKeysFromDataFile(datFile)) diff --git a/generator/routing_index_generator.cpp b/generator/routing_index_generator.cpp index 6db5e03755..e141dee0f8 100644 --- a/generator/routing_index_generator.cpp +++ b/generator/routing_index_generator.cpp @@ -1,5 +1,10 @@ #include "generator/routing_index_generator.hpp" +#include "generator/borders_generator.hpp" +#include "generator/borders_loader.hpp" + +#include "routing/cross_mwm_connector.hpp" +#include "routing/cross_mwm_connector_serialization.hpp" #include "routing/index_graph.hpp" #include "routing/index_graph_serialization.hpp" #include "routing/vehicle_mask.hpp" @@ -8,19 +13,22 @@ #include "routing_common/car_model.hpp" #include "routing_common/pedestrian_model.hpp" +#include "indexer/coding_params.hpp" +#include "indexer/data_header.hpp" #include "indexer/feature.hpp" #include "indexer/feature_processor.hpp" #include "indexer/point_to_int64.hpp" #include "coding/file_container.hpp" +#include "coding/file_name_utils.hpp" #include "base/checked_cast.hpp" #include "base/logging.hpp" -#include "std/bind.hpp" -#include "std/shared_ptr.hpp" -#include "std/unordered_map.hpp" -#include "std/vector.hpp" +#include <functional> +#include <memory> +#include <unordered_map> +#include <vector> using namespace feature; using namespace platform; @@ -28,10 +36,10 @@ using namespace routing; namespace { -class Processor final +class VehicleMaskBuilder final { public: - explicit Processor(string const & country) + explicit VehicleMaskBuilder(string const & country) : m_pedestrianModel(PedestrianModelFactory().GetVehicleModelForCountry(country)) , m_bicycleModel(BicycleModelFactory().GetVehicleModelForCountry(country)) , m_carModel(CarModelFactory().GetVehicleModelForCountry(country)) @@ -41,6 +49,42 @@ public: CHECK(m_carModel, ()); } + VehicleMask CalcRoadMask(FeatureType const & f) const + { + return CalcMask( + f, [&](IVehicleModel const & model, FeatureType const & f) { return model.IsRoad(f); }); + } + + VehicleMask CalcOneWayMask(FeatureType const & f) const + { + return CalcMask( + f, [&](IVehicleModel const & model, FeatureType const & f) { return model.IsOneWay(f); }); + } + +private: + template <class Fn> + VehicleMask CalcMask(FeatureType const & f, Fn && fn) const + { + VehicleMask mask = 0; + if (fn(*m_pedestrianModel, f)) + mask |= kPedestrianMask; + if (fn(*m_bicycleModel, f)) + mask |= kBicycleMask; + if (fn(*m_carModel, f)) + mask |= kCarMask; + + return mask; + } + + shared_ptr<IVehicleModel> const m_pedestrianModel; + shared_ptr<IVehicleModel> const m_bicycleModel; + shared_ptr<IVehicleModel> const m_carModel; +}; + +class Processor final +{ +public: + explicit Processor(string const & country) : m_maskBuilder(country) {} void ProcessAllFeatures(string const & filename) { feature::ForEachFromDat(filename, bind(&Processor::ProcessFeature, this, _1, _2)); @@ -60,11 +104,10 @@ public: } unordered_map<uint32_t, VehicleMask> const & GetMasks() const { return m_masks; } - private: void ProcessFeature(FeatureType const & f, uint32_t id) { - VehicleMask const mask = CalcVehicleMask(f); + VehicleMask const mask = m_maskBuilder.CalcRoadMask(f); if (mask == 0) return; @@ -78,25 +121,79 @@ private: } } - VehicleMask CalcVehicleMask(FeatureType const & f) const - { - VehicleMask mask = 0; - if (m_pedestrianModel->IsRoad(f)) - mask |= kPedestrianMask; - if (m_bicycleModel->IsRoad(f)) - mask |= kBicycleMask; - if (m_carModel->IsRoad(f)) - mask |= kCarMask; - - return mask; - } - - shared_ptr<IVehicleModel> const m_pedestrianModel; - shared_ptr<IVehicleModel> const m_bicycleModel; - shared_ptr<IVehicleModel> const m_carModel; + VehicleMaskBuilder const m_maskBuilder; unordered_map<uint64_t, Joint> m_posToJoint; unordered_map<uint32_t, VehicleMask> m_masks; }; + +bool RegionsContain(vector<m2::RegionD> const & regions, m2::PointD const & point) +{ + for (auto const & region : regions) + { + if (region.Contains(point)) + return true; + } + + return false; +} + +void CalcCrossMwmTransitions(string const & path, string const & mwmFile, string const & country, + vector<CrossMwmConnectorSerializer::Transition> & transitions, + CrossMwmConnectorPerVehicleType & connectors) +{ + string const polyFile = my::JoinPath(path, BORDERS_DIR, country + BORDERS_EXTENSION); + vector<m2::RegionD> borders; + osm::LoadBorders(polyFile, borders); + + VehicleMaskBuilder const maskMaker(country); + + feature::ForEachFromDat(mwmFile, [&](FeatureType const & f, uint32_t featureId) { + VehicleMask const roadMask = maskMaker.CalcRoadMask(f); + if (roadMask == 0) + return; + + f.ParseGeometry(FeatureType::BEST_GEOMETRY); + size_t const pointsCount = f.GetPointsCount(); + if (pointsCount == 0) + return; + + bool prevPointIn = RegionsContain(borders, f.GetPoint(0)); + + for (size_t i = 1; i < pointsCount; ++i) + { + bool const currPointIn = RegionsContain(borders, f.GetPoint(i)); + if (currPointIn == prevPointIn) + continue; + + uint32_t const segmentIdx = base::asserted_cast<uint32_t>(i - 1); + VehicleMask const oneWayMask = maskMaker.CalcOneWayMask(f); + + transitions.emplace_back(featureId, segmentIdx, roadMask, oneWayMask, currPointIn, + f.GetPoint(i - 1), f.GetPoint(i)); + + for (size_t j = 0; j < connectors.size(); ++j) + { + VehicleMask const mask = GetVehicleMask(static_cast<VehicleType>(j)); + CrossMwmConnectorSerializer::AddTransition(transitions.back(), mask, connectors[j]); + } + + prevPointIn = currPointIn; + } + }); +} + +void FillWeights(string const & path, string const & country, CrossMwmConnector & connector) +{ + shared_ptr<IVehicleModel> vehicleModel = CarModelFactory().GetVehicleModelForCountry(country); + shared_ptr<EdgeEstimator> estimator = + EdgeEstimator::CreateForCar(nullptr /* trafficStash */, vehicleModel->GetMaxSpeed()); + + connector.FillWeights([&](Segment const & enter, Segment const & exit) { + // TODO replace fake weights with weights calculated by routing. + return estimator->CalcHeuristic(connector.GetPoint(enter, true /* front */), + connector.GetPoint(exit, true /* front */)); + }); +} } // namespace namespace routing @@ -129,4 +226,30 @@ bool BuildRoutingIndex(string const & filename, string const & country) return false; } } + +void BuildCrossMwmSection(string const & path, string const & mwmFile, string const & country) +{ + LOG(LINFO, ("Building cross mwm section for", country)); + my::Timer timer; + + CrossMwmConnectorPerVehicleType connectors; + + vector<CrossMwmConnectorSerializer::Transition> transitions; + CalcCrossMwmTransitions(path, mwmFile, country, transitions, connectors); + + FillWeights(path, country, connectors[static_cast<size_t>(VehicleType::Car)]); + + FilesContainerW cont(mwmFile, FileWriter::OP_WRITE_EXISTING); + FileWriter writer = cont.GetWriter(CROSS_MWM_FILE_TAG); + + DataHeader const dataHeader(mwmFile); + serial::CodingParams const & codingParams = dataHeader.GetDefCodingParams(); + + auto const startPos = writer.Pos(); + CrossMwmConnectorSerializer::Serialize(transitions, connectors, codingParams, writer); + auto const sectionSize = writer.Pos() - startPos; + + LOG(LINFO, ("Cross mwm section for", country, "generated in", timer.ElapsedSeconds(), + "seconds, section size:", sectionSize, "bytes, transitions:", transitions.size())); +} } // namespace routing diff --git a/generator/routing_index_generator.hpp b/generator/routing_index_generator.hpp index f5158c904d..6d70078618 100644 --- a/generator/routing_index_generator.hpp +++ b/generator/routing_index_generator.hpp @@ -5,4 +5,5 @@ namespace routing { bool BuildRoutingIndex(string const & filename, string const & country); +void BuildCrossMwmSection(string const & path, string const & mwmFile, string const & country); } // namespace routing diff --git a/map/framework.cpp b/map/framework.cpp index 6094a12a64..1656ca4631 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -1089,9 +1089,10 @@ void Framework::SetVisibleViewport(m2::RectD const & rect) m_drapeEngine->SetVisibleViewport(rect); } -void Framework::ShowRect(m2::RectD const & rect, int maxScale) +void Framework::ShowRect(m2::RectD const & rect, int maxScale, bool animation) { - CallDrapeFunction(bind(&df::DrapeEngine::SetModelViewRect, _1, rect, true, maxScale, true)); + CallDrapeFunction(bind(&df::DrapeEngine::SetModelViewRect, _1, rect, true /* applyRotation */, + maxScale /* zoom */, animation)); } void Framework::ShowRect(m2::AnyRectD const & rect) diff --git a/map/framework.hpp b/map/framework.hpp index 3c380674ae..5a85619b38 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -275,6 +275,9 @@ public: Index const & GetIndex() const { return m_model.GetIndex(); } + search::Engine & GetSearchEngine() { return *m_searchEngine; } + search::Engine const & GetSearchEngine() const { return *m_searchEngine; } + /// @name Bookmarks, Tracks and other UserMarks //@{ /// Scans and loads all kml files with bookmarks in WritableDir. @@ -571,7 +574,7 @@ public: void SetVisibleViewport(m2::RectD const & rect); /// - Check minimal visible scale according to downloaded countries. - void ShowRect(m2::RectD const & rect, int maxScale = -1); + void ShowRect(m2::RectD const & rect, int maxScale = -1, bool animation = true); void ShowRect(m2::AnyRectD const & rect); void GetTouchRect(m2::PointD const & center, uint32_t pxRadius, m2::AnyRectD & rect); diff --git a/routing/CMakeLists.txt b/routing/CMakeLists.txt index be86001356..16b4374e17 100644 --- a/routing/CMakeLists.txt +++ b/routing/CMakeLists.txt @@ -18,6 +18,10 @@ set( bicycle_directions.hpp car_router.cpp car_router.hpp + cross_mwm_connector.cpp + cross_mwm_connector.hpp + cross_mwm_connector_serialization.cpp + cross_mwm_connector_serialization.hpp cross_mwm_index_graph.cpp cross_mwm_index_graph.hpp cross_mwm_road_graph.cpp diff --git a/routing/cross_mwm_connector.cpp b/routing/cross_mwm_connector.cpp new file mode 100644 index 0000000000..cf6d62469f --- /dev/null +++ b/routing/cross_mwm_connector.cpp @@ -0,0 +1,136 @@ +#include "routing/cross_mwm_connector.hpp" + +namespace +{ +uint32_t constexpr kFakeId = std::numeric_limits<uint32_t>::max(); +} // namespace + +namespace routing +{ +// static +CrossMwmConnector::Weight constexpr CrossMwmConnector::kNoRoute; + +void CrossMwmConnector::AddTransition(uint32_t featureId, uint32_t segmentIdx, bool oneWay, + bool forwardIsEnter, m2::PointD const & backPoint, + m2::PointD const & frontPoint) +{ + Transition transition(kFakeId, kFakeId, oneWay, forwardIsEnter, backPoint, frontPoint); + + if (forwardIsEnter) + { + transition.m_enterIdx = base::asserted_cast<uint32_t>(m_enters.size()); + m_enters.emplace_back(m_mwmId, featureId, segmentIdx, true /* forward */); + } + else + { + transition.m_exitIdx = base::asserted_cast<uint32_t>(m_exits.size()); + m_exits.emplace_back(m_mwmId, featureId, segmentIdx, true /* forward */); + } + + if (!oneWay) + { + if (forwardIsEnter) + { + transition.m_exitIdx = base::asserted_cast<uint32_t>(m_exits.size()); + m_exits.emplace_back(m_mwmId, featureId, segmentIdx, false /* forward */); + } + else + { + transition.m_enterIdx = base::asserted_cast<uint32_t>(m_enters.size()); + m_enters.emplace_back(m_mwmId, featureId, segmentIdx, false /* forward */); + } + } + + m_transitions[Key(featureId, segmentIdx)] = transition; +} + +bool CrossMwmConnector::IsTransition(Segment const & segment, bool isOutgoing) const +{ + auto it = m_transitions.find(Key(segment.GetFeatureId(), segment.GetSegmentIdx())); + if (it == m_transitions.cend()) + return false; + + Transition const & transition = it->second; + if (transition.m_oneWay && !segment.IsForward()) + return false; + + return (segment.IsForward() == transition.m_forwardIsEnter) == isOutgoing; +} + +m2::PointD const & CrossMwmConnector::GetPoint(Segment const & segment, bool front) const +{ + Transition const & transition = GetTransition(segment); + return segment.IsForward() == front ? transition.m_frontPoint : transition.m_backPoint; +} + +void CrossMwmConnector::GetEdgeList(Segment const & segment, bool isOutgoing, + std::vector<SegmentEdge> & edges) const +{ + Transition const & transition = GetTransition(segment); + if (isOutgoing) + { + ASSERT_NOT_EQUAL(transition.m_enterIdx, kFakeId, ()); + for (size_t exitIdx = 0; exitIdx < m_exits.size(); ++exitIdx) + { + Weight const weight = GetWeight(base::asserted_cast<size_t>(transition.m_enterIdx), exitIdx); + AddEdge(m_exits[exitIdx], weight, edges); + } + } + else + { + ASSERT_NOT_EQUAL(transition.m_exitIdx, kFakeId, ()); + for (size_t enterIdx = 0; enterIdx < m_enters.size(); ++enterIdx) + { + Weight const weight = GetWeight(enterIdx, base::asserted_cast<size_t>(transition.m_exitIdx)); + AddEdge(m_enters[enterIdx], weight, edges); + } + } +} + +bool CrossMwmConnector::WeightsWereLoaded() const +{ + switch (m_weightsLoadState) + { + case WeightsLoadState::Unknown: + case WeightsLoadState::ReadyToLoad: return false; + case WeightsLoadState::NotExists: + case WeightsLoadState::Loaded: return true; + } +} + +std::string DebugPrint(CrossMwmConnector::WeightsLoadState state) +{ + switch (state) + { + case CrossMwmConnector::WeightsLoadState::Unknown: return "Unknown"; + case CrossMwmConnector::WeightsLoadState::ReadyToLoad: return "ReadyToLoad"; + case CrossMwmConnector::WeightsLoadState::NotExists: return "NotExists"; + case CrossMwmConnector::WeightsLoadState::Loaded: return "Loaded"; + } +} + +void CrossMwmConnector::AddEdge(Segment const & segment, Weight weight, + std::vector<SegmentEdge> & edges) const +{ + if (weight != kNoRoute) + edges.emplace_back(segment, static_cast<double>(weight)); +} + +CrossMwmConnector::Transition const & CrossMwmConnector::GetTransition( + Segment const & segment) const +{ + auto it = m_transitions.find(Key(segment.GetFeatureId(), segment.GetSegmentIdx())); + CHECK(it != m_transitions.cend(), ("Not a transition segment:", segment)); + return it->second; +} + +CrossMwmConnector::Weight CrossMwmConnector::GetWeight(size_t enterIdx, size_t exitIdx) const +{ + ASSERT_LESS(enterIdx, m_enters.size(), ()); + ASSERT_LESS(exitIdx, m_exits.size(), ()); + + size_t const i = enterIdx * m_exits.size() + exitIdx; + ASSERT_LESS(i, m_weights.size(), ()); + return m_weights[i]; +} +} // namespace routing diff --git a/routing/cross_mwm_connector.hpp b/routing/cross_mwm_connector.hpp new file mode 100644 index 0000000000..22ef84de61 --- /dev/null +++ b/routing/cross_mwm_connector.hpp @@ -0,0 +1,139 @@ +#pragma once + +#include "routing/segment.hpp" + +#include "geometry/point2d.hpp" + +#include "base/assert.hpp" + +#include <cmath> +#include <limits> +#include <unordered_map> +#include <vector> + +namespace routing +{ +class CrossMwmConnector final +{ +public: + CrossMwmConnector() : m_mwmId(kFakeNumMwmId) {} + explicit CrossMwmConnector(NumMwmId mwmId) : m_mwmId(mwmId) {} + + void AddTransition(uint32_t featureId, uint32_t segmentIdx, bool oneWay, bool forwardIsEnter, + m2::PointD const & backPoint, m2::PointD const & frontPoint); + + bool IsTransition(Segment const & segment, bool isOutgoing) const; + m2::PointD const & GetPoint(Segment const & segment, bool front) const; + void GetEdgeList(Segment const & segment, bool isOutgoing, + std::vector<SegmentEdge> & edges) const; + + std::vector<Segment> const & GetEnters() const { return m_enters; } + std::vector<Segment> const & GetExits() const { return m_exits; } + bool HasWeights() const { return !m_weights.empty(); } + bool WeightsWereLoaded() const; + + template <typename CalcWeight> + void FillWeights(CalcWeight && calcWeight) + { + CHECK_EQUAL(m_weightsLoadState, WeightsLoadState::Unknown, ()); + CHECK(m_weights.empty(), ()); + + m_weights.reserve(m_enters.size() * m_exits.size()); + for (Segment const & enter : m_enters) + { + for (Segment const & exit : m_exits) + { + double const weight = calcWeight(enter, exit); + // Edges weights should be >= astar heuristic, so use std::ceil. + m_weights.push_back(static_cast<Weight>(std::ceil(weight))); + } + } + } + +private: + // This is an internal type for storing edges weights. + // Weight is the time requred for the route to pass. + // Weight is measured in seconds rounded upwards. + using Weight = uint32_t; + + static Weight constexpr kNoRoute = 0; + + struct Key + { + Key() = default; + + Key(uint32_t featureId, uint32_t segmentIdx) : m_featureId(featureId), m_segmentIdx(segmentIdx) + { + } + + bool operator==(Key const & key) const + { + return m_featureId == key.m_featureId && m_segmentIdx == key.m_segmentIdx; + } + + uint32_t m_featureId = 0; + uint32_t m_segmentIdx = 0; + }; + + struct HashKey + { + size_t operator()(Key const & key) const + { + return std::hash<uint64_t>()((static_cast<uint64_t>(key.m_featureId) << 32) + + static_cast<uint64_t>(key.m_segmentIdx)); + } + }; + + struct Transition + { + Transition() = default; + + Transition(uint32_t enterIdx, uint32_t exitIdx, bool oneWay, bool forwardIsEnter, + m2::PointD const & backPoint, m2::PointD const & frontPoint) + : m_enterIdx(enterIdx) + , m_exitIdx(exitIdx) + , m_backPoint(backPoint) + , m_frontPoint(frontPoint) + , m_oneWay(oneWay) + , m_forwardIsEnter(forwardIsEnter) + { + } + + uint32_t m_enterIdx = 0; + uint32_t m_exitIdx = 0; + // Endpoints of transition segment. + // m_backPoint = points[segmentIdx] + // m_frontPoint = points[segmentIdx + 1] + m2::PointD m_backPoint = m2::PointD::Zero(); + m2::PointD m_frontPoint = m2::PointD::Zero(); + bool m_oneWay = false; + // Transition represents both forward and backward segments with same featureId, segmentIdx. + // m_forwardIsEnter == true means: forward segment is enter to mwm: + // Enter means: m_backPoint is outside mwm borders, m_frontPoint is inside. + bool m_forwardIsEnter = false; + }; + + enum class WeightsLoadState + { + Unknown, + NotExists, + ReadyToLoad, + Loaded + }; + + friend class CrossMwmConnectorSerializer; + friend std::string DebugPrint(WeightsLoadState state); + + void AddEdge(Segment const & segment, Weight weight, std::vector<SegmentEdge> & edges) const; + Transition const & GetTransition(Segment const & segment) const; + Weight GetWeight(size_t enterIdx, size_t exitIdx) const; + + NumMwmId const m_mwmId; + std::vector<Segment> m_enters; + std::vector<Segment> m_exits; + std::unordered_map<Key, Transition, HashKey> m_transitions; + WeightsLoadState m_weightsLoadState = WeightsLoadState::Unknown; + uint64_t m_weightsOffset = 0; + std::vector<Weight> m_weights; +}; +} // namespace routing diff --git a/routing/cross_mwm_connector_serialization.cpp b/routing/cross_mwm_connector_serialization.cpp new file mode 100644 index 0000000000..cdc6c2c4ec --- /dev/null +++ b/routing/cross_mwm_connector_serialization.cpp @@ -0,0 +1,30 @@ +#include "routing/cross_mwm_connector_serialization.hpp" + +using namespace std; + +namespace routing +{ +// static +uint32_t constexpr CrossMwmConnectorSerializer::kLastVersion; + +// static +void CrossMwmConnectorSerializer::WriteTransitions(vector<Transition> const & transitions, + serial::CodingParams const & codingParams, + uint8_t bitsPerMask, vector<uint8_t> & buffer) +{ + MemWriter<vector<uint8_t>> memWriter(buffer); + + for (Transition const & transition : transitions) + transition.Serialize(codingParams, bitsPerMask, memWriter); +} + +// static +void CrossMwmConnectorSerializer::WriteWeights(vector<CrossMwmConnector::Weight> const & weights, + vector<uint8_t> & buffer) +{ + MemWriter<vector<uint8_t>> memWriter(buffer); + + for (auto weight : weights) + WriteToSink(memWriter, weight); +} +} // namespace routing diff --git a/routing/cross_mwm_connector_serialization.hpp b/routing/cross_mwm_connector_serialization.hpp new file mode 100644 index 0000000000..f21bc74a7d --- /dev/null +++ b/routing/cross_mwm_connector_serialization.hpp @@ -0,0 +1,353 @@ +#pragma once + +#include "routing/cross_mwm_connector.hpp" +#include "routing/routing_exceptions.hpp" +#include "routing/vehicle_mask.hpp" + +#include "indexer/coding_params.hpp" +#include "indexer/geometry_serialization.hpp" + +#include "coding/bit_streams.hpp" +#include "coding/reader.hpp" +#include "coding/write_to_sink.hpp" +#include "coding/writer.hpp" + +#include "base/checked_cast.hpp" + +#include <array> +#include <cstdint> +#include <limits> +#include <vector> + +namespace routing +{ +using CrossMwmConnectorPerVehicleType = + std::array<CrossMwmConnector, static_cast<size_t>(VehicleType::Count)>; + +class CrossMwmConnectorSerializer final +{ +public: + class Transition final + { + public: + Transition() = default; + + Transition(uint32_t featureId, uint32_t segmentIdx, VehicleMask roadMask, + VehicleMask oneWayMask, bool forwardIsEnter, m2::PointD const & backPoint, + m2::PointD const & frontPoint) + : m_featureId(featureId) + , m_segmentIdx(segmentIdx) + , m_backPoint(backPoint) + , m_frontPoint(frontPoint) + , m_roadMask(roadMask) + , m_oneWayMask(oneWayMask) + , m_forwardIsEnter(forwardIsEnter) + { + } + + template <class Sink> + void Serialize(serial::CodingParams const & codingParams, uint8_t bitsPerMask, + Sink & sink) const + { + WriteToSink(sink, m_featureId); + WriteToSink(sink, m_segmentIdx); + serial::SavePoint(sink, m_backPoint, codingParams); + serial::SavePoint(sink, m_frontPoint, codingParams); + + BitWriter<Sink> writer(sink); + writer.WriteAtMost32Bits(base::asserted_cast<uint32_t>(m_roadMask), bitsPerMask); + writer.WriteAtMost32Bits(base::asserted_cast<uint32_t>(m_oneWayMask), bitsPerMask); + writer.Write(m_forwardIsEnter ? 0 : 1, 1); + } + + template <class Source> + void Deserialize(serial::CodingParams const & codingParams, uint8_t bitsPerMask, Source & src) + { + m_featureId = ReadPrimitiveFromSource<decltype(m_featureId)>(src); + m_segmentIdx = ReadPrimitiveFromSource<decltype(m_segmentIdx)>(src); + m_backPoint = serial::LoadPoint(src, codingParams); + m_frontPoint = serial::LoadPoint(src, codingParams); + + BitReader<Source> reader(src); + m_roadMask = base::asserted_cast<VehicleMask>(reader.ReadAtMost32Bits(bitsPerMask)); + m_oneWayMask = base::asserted_cast<VehicleMask>(reader.ReadAtMost32Bits(bitsPerMask)); + m_forwardIsEnter = reader.Read(1) == 0; + } + + uint32_t GetFeatureId() const { return m_featureId; } + uint32_t GetSegmentIdx() const { return m_segmentIdx; } + m2::PointD const & GetBackPoint() const { return m_backPoint; } + m2::PointD const & GetFrontPoint() const { return m_frontPoint; } + bool ForwardIsEnter() const { return m_forwardIsEnter; } + VehicleMask GetRoadMask() const { return m_roadMask; } + VehicleMask GetOneWayMask() const { return m_oneWayMask; } + + private: + uint32_t m_featureId = 0; + uint32_t m_segmentIdx = 0; + m2::PointD m_backPoint = m2::PointD::Zero(); + m2::PointD m_frontPoint = m2::PointD::Zero(); + VehicleMask m_roadMask = 0; + VehicleMask m_oneWayMask = 0; + bool m_forwardIsEnter = false; + }; + + CrossMwmConnectorSerializer() = delete; + + template <class Sink> + static void Serialize(std::vector<Transition> const & transitions, + CrossMwmConnectorPerVehicleType const & connectors, + serial::CodingParams const & codingParams, Sink & sink) + { + auto const bitsPerMask = GetBitsPerMask<uint8_t>(); + std::vector<uint8_t> transitionsBuf; + WriteTransitions(transitions, codingParams, bitsPerMask, transitionsBuf); + + Header header(base::checked_cast<uint32_t>(transitions.size()), + base::checked_cast<uint64_t>(transitionsBuf.size()), codingParams, bitsPerMask); + std::vector<std::vector<uint8_t>> weightBuffers(connectors.size()); + + for (size_t i = 0; i < connectors.size(); ++i) + { + CrossMwmConnector const & connector = connectors[i]; + if (connector.m_weights.empty()) + continue; + + std::vector<uint8_t> & buffer = weightBuffers[i]; + WriteWeights(connector.m_weights, buffer); + + auto const numEnters = base::checked_cast<uint32_t>(connector.GetEnters().size()); + auto const numExits = base::checked_cast<uint32_t>(connector.GetExits().size()); + auto const vehicleType = static_cast<VehicleType>(i); + header.AddSection(Section(buffer.size(), numEnters, numExits, vehicleType)); + } + + header.Serialize(sink); + FlushBuffer(transitionsBuf, sink); + + for (auto & buffer : weightBuffers) + FlushBuffer(buffer, sink); + } + + template <class Source> + static void DeserializeTransitions(VehicleType requiredVehicle, CrossMwmConnector & connector, + Source & src) + { + CHECK(connector.m_weightsLoadState == CrossMwmConnector::WeightsLoadState::Unknown, ()); + + Header header; + header.Deserialize(src); + + uint64_t weightsOffset = src.Pos() + header.GetSizeTransitions(); + VehicleMask const requiredMask = GetVehicleMask(requiredVehicle); + auto const numTransitions = base::checked_cast<size_t>(header.GetNumTransitions()); + + for (size_t i = 0; i < numTransitions; ++i) + { + Transition transition; + transition.Deserialize(header.GetCodingParams(), header.GetBitsPerMask(), src); + AddTransition(transition, requiredMask, connector); + } + + if (src.Pos() != weightsOffset) + { + MYTHROW(CorruptedDataException, + ("Wrong position", src.Pos(), "after decoding transitions, expected:", + connector.m_weightsOffset, "size:", header.GetSizeTransitions())); + } + + for (Section const & section : header.GetSections()) + { + if (section.GetVehicleType() != requiredVehicle) + { + weightsOffset += section.GetSize(); + continue; + } + + size_t const numEnters = connector.GetEnters().size(); + size_t const numExits = connector.GetExits().size(); + + if (base::checked_cast<size_t>(section.GetNumEnters()) != numEnters) + { + MYTHROW(CorruptedDataException, ("Mismatch enters number, section:", section.GetNumEnters(), + ", connector:", numEnters)); + } + + if (base::checked_cast<size_t>(section.GetNumExits()) != numExits) + { + MYTHROW(CorruptedDataException, ("Mismatch exits number, section:", section.GetNumExits(), + ", connector:", numExits)); + } + + connector.m_weightsOffset = weightsOffset; + connector.m_weightsLoadState = CrossMwmConnector::WeightsLoadState::ReadyToLoad; + return; + } + + connector.m_weightsLoadState = CrossMwmConnector::WeightsLoadState::NotExists; + } + + template <class Source> + static void DeserializeWeights(VehicleType requiredVehicle, CrossMwmConnector & connector, + Source & src) + { + CHECK(connector.m_weightsLoadState == CrossMwmConnector::WeightsLoadState::ReadyToLoad, ()); + + src.Skip(connector.m_weightsOffset); + + size_t const amount = connector.GetEnters().size() * connector.GetExits().size(); + connector.m_weights.reserve(amount); + for (size_t i = 0; i < amount; ++i) + { + auto const weight = ReadPrimitiveFromSource<uint32_t>(src); + connector.m_weights.push_back(static_cast<CrossMwmConnector::Weight>(weight)); + } + + connector.m_weightsLoadState = CrossMwmConnector::WeightsLoadState::Loaded; + } + + static void AddTransition(Transition const & transition, VehicleMask requiredMask, + CrossMwmConnector & connector) + { + if ((transition.GetRoadMask() & requiredMask) == 0) + return; + + bool const isOneWay = (transition.GetOneWayMask() & requiredMask) != 0; + connector.AddTransition(transition.GetFeatureId(), transition.GetSegmentIdx(), isOneWay, + transition.ForwardIsEnter(), transition.GetBackPoint(), + transition.GetFrontPoint()); + } + +private: + static uint32_t constexpr kLastVersion = 0; + + class Section final + { + public: + Section() = default; + + Section(uint64_t size, uint32_t numEnters, uint32_t numExits, VehicleType vehicleType) + : m_size(size), m_numEnters(numEnters), m_numExits(numExits), m_vehicleType(vehicleType) + { + } + + template <class Sink> + void Serialize(Sink & sink) const + { + WriteToSink(sink, m_size); + WriteToSink(sink, m_numEnters); + WriteToSink(sink, m_numExits); + WriteToSink(sink, static_cast<uint8_t>(m_vehicleType)); + } + + template <class Source> + void Deserialize(Source & src) + { + m_size = ReadPrimitiveFromSource<decltype(m_size)>(src); + m_numEnters = ReadPrimitiveFromSource<decltype(m_numEnters)>(src); + m_numExits = ReadPrimitiveFromSource<decltype(m_numExits)>(src); + m_vehicleType = static_cast<VehicleType>(ReadPrimitiveFromSource<uint8_t>(src)); + } + + uint64_t GetSize() const { return m_size; } + uint32_t GetNumEnters() const { return m_numEnters; } + uint32_t GetNumExits() const { return m_numExits; } + VehicleType GetVehicleType() const { return m_vehicleType; } + + private: + uint64_t m_size = 0; + uint32_t m_numEnters = 0; + uint32_t m_numExits = 0; + VehicleType m_vehicleType = VehicleType::Pedestrian; + }; + + class Header final + { + public: + Header() = default; + + Header(uint32_t numTransitions, uint64_t sizeTransitions, + serial::CodingParams const & codingParams, uint8_t bitsPerMask) + : m_numTransitions(numTransitions) + , m_sizeTransitions(sizeTransitions) + , m_codingParams(codingParams) + , m_bitsPerMask(bitsPerMask) + { + } + + template <class Sink> + void Serialize(Sink & sink) const + { + WriteToSink(sink, m_version); + WriteToSink(sink, m_numTransitions); + WriteToSink(sink, m_sizeTransitions); + m_codingParams.Save(sink); + WriteToSink(sink, m_bitsPerMask); + + WriteToSink(sink, base::checked_cast<uint32_t>(m_sections.size())); + for (Section const & section : m_sections) + section.Serialize(sink); + } + + template <class Source> + void Deserialize(Source & src) + { + m_version = ReadPrimitiveFromSource<decltype(m_version)>(src); + if (m_version != kLastVersion) + { + MYTHROW(CorruptedDataException, ("Unknown cross mwm section version ", m_version, + ", current version ", kLastVersion)); + } + + m_numTransitions = ReadPrimitiveFromSource<decltype(m_numTransitions)>(src); + m_sizeTransitions = ReadPrimitiveFromSource<decltype(m_sizeTransitions)>(src); + m_codingParams.Load(src); + m_bitsPerMask = ReadPrimitiveFromSource<decltype(m_bitsPerMask)>(src); + + auto const sectionsSize = ReadPrimitiveFromSource<uint32_t>(src); + m_sections.resize(base::checked_cast<size_t>(sectionsSize)); + for (Section & section : m_sections) + section.Deserialize(src); + } + + void AddSection(Section const & section) { m_sections.push_back(section); } + + uint32_t GetNumTransitions() const { return m_numTransitions; } + uint64_t GetSizeTransitions() const { return m_sizeTransitions; } + serial::CodingParams const & GetCodingParams() const { return m_codingParams; } + uint8_t GetBitsPerMask() const { return m_bitsPerMask; } + std::vector<Section> const & GetSections() const { return m_sections; } + + private: + uint32_t m_version = kLastVersion; + uint32_t m_numTransitions = 0; + uint64_t m_sizeTransitions = 0; + serial::CodingParams m_codingParams; + uint8_t m_bitsPerMask = 0; + std::vector<Section> m_sections; + }; + + template <typename T> + static T GetBitsPerMask() + { + static_assert( + static_cast<size_t>(VehicleType::Count) <= static_cast<size_t>(numeric_limits<T>::max()), + "Can't pack VehicleType::Count into chosen type"); + return static_cast<T>(VehicleType::Count); + } + + template <class Sink> + static void FlushBuffer(std::vector<uint8_t> & buffer, Sink & sink) + { + sink.Write(buffer.data(), buffer.size()); + buffer.clear(); + } + + static void WriteTransitions(std::vector<Transition> const & transitions, + serial::CodingParams const & codingParams, uint8_t bitsPerMask, + std::vector<uint8_t> & buffer); + + static void WriteWeights(std::vector<CrossMwmConnector::Weight> const & weights, + std::vector<uint8_t> & buffer); +}; +} // namespace routing diff --git a/routing/edge_estimator.cpp b/routing/edge_estimator.cpp index e1248ddb28..3a3f8dd01b 100644 --- a/routing/edge_estimator.cpp +++ b/routing/edge_estimator.cpp @@ -73,17 +73,20 @@ double CarEdgeEstimator::CalcSegmentWeight(Segment const & segment, RoadGeometry road.GetPoint(segment.GetPointId(true /* front */)), speedMPS) * kTimePenalty; - auto const * trafficColoring = m_trafficStash->Get(segment.GetMwmId()); - if (trafficColoring) + if (m_trafficStash) { - auto const dir = segment.IsForward() ? TrafficInfo::RoadSegmentId::kForwardDirection - : TrafficInfo::RoadSegmentId::kReverseDirection; - auto const it = trafficColoring->find( - TrafficInfo::RoadSegmentId(segment.GetFeatureId(), segment.GetSegmentIdx(), dir)); - SpeedGroup const speedGroup = - (it == trafficColoring->cend()) ? SpeedGroup::Unknown : it->second; - ASSERT_LESS(speedGroup, SpeedGroup::Count, ()); - result *= CalcTrafficFactor(speedGroup); + auto const * trafficColoring = m_trafficStash->Get(segment.GetMwmId()); + if (trafficColoring) + { + auto const dir = segment.IsForward() ? TrafficInfo::RoadSegmentId::kForwardDirection + : TrafficInfo::RoadSegmentId::kReverseDirection; + auto const it = trafficColoring->find( + TrafficInfo::RoadSegmentId(segment.GetFeatureId(), segment.GetSegmentIdx(), dir)); + SpeedGroup const speedGroup = + (it == trafficColoring->cend()) ? SpeedGroup::Unknown : it->second; + ASSERT_LESS(speedGroup, SpeedGroup::Count, ()); + result *= CalcTrafficFactor(speedGroup); + } } return result; diff --git a/routing/routing.pro b/routing/routing.pro index 6441f1a865..7ba284b0a5 100644 --- a/routing/routing.pro +++ b/routing/routing.pro @@ -17,6 +17,8 @@ SOURCES += \ base/followed_polyline.cpp \ bicycle_directions.cpp \ car_router.cpp \ + cross_mwm_connector.cpp \ + cross_mwm_connector_serialization.cpp \ cross_mwm_index_graph.cpp \ cross_mwm_road_graph.cpp \ cross_mwm_router.cpp \ @@ -67,6 +69,8 @@ HEADERS += \ base/followed_polyline.hpp \ bicycle_directions.hpp \ car_router.hpp \ + cross_mwm_connector.hpp \ + cross_mwm_connector_serialization.hpp \ cross_mwm_index_graph.hpp \ cross_mwm_road_graph.hpp \ cross_mwm_router.hpp \ diff --git a/routing/routing_tests/CMakeLists.txt b/routing/routing_tests/CMakeLists.txt index b960860ef9..9c1af798b4 100644 --- a/routing/routing_tests/CMakeLists.txt +++ b/routing/routing_tests/CMakeLists.txt @@ -8,6 +8,7 @@ set( astar_progress_test.cpp astar_router_test.cpp async_router_test.cpp + cross_mwm_connector_test.cpp cross_routing_tests.cpp cumulative_restriction_test.cpp followed_polyline_test.cpp diff --git a/routing/routing_tests/cross_mwm_connector_test.cpp b/routing/routing_tests/cross_mwm_connector_test.cpp new file mode 100644 index 0000000000..7973ed088c --- /dev/null +++ b/routing/routing_tests/cross_mwm_connector_test.cpp @@ -0,0 +1,239 @@ +#include "testing/testing.hpp" + +#include "routing/cross_mwm_connector_serialization.hpp" + +#include "coding/writer.hpp" + +using namespace routing; +using namespace std; + +namespace +{ +NumMwmId constexpr mwmId = 777; + +void TestConnectorConsistency(CrossMwmConnector const & connector) +{ + for (Segment const & enter : connector.GetEnters()) + { + TEST(connector.IsTransition(enter, true /* isOutgoing */), ("enter:", enter)); + TEST(!connector.IsTransition(enter, false /* isOutgoing */), ("enter:", enter)); + } + + for (Segment const & exit : connector.GetExits()) + { + TEST(!connector.IsTransition(exit, true /* isOutgoing */), ("exit:", exit)); + TEST(connector.IsTransition(exit, false /* isOutgoing */), ("exit:", exit)); + } +} + +void TestEdges(CrossMwmConnector const & connector, Segment const & from, bool isOutgoing, + vector<SegmentEdge> const & expectedEdges) +{ + vector<SegmentEdge> edges; + connector.GetEdgeList(from, isOutgoing, edges); + TEST_EQUAL(edges, expectedEdges, ()); +} +} + +namespace routing_test +{ +UNIT_TEST(OneWayEnter) +{ + uint32_t constexpr featureId = 1; + uint32_t constexpr segmentIdx = 1; + CrossMwmConnector connector(mwmId); + connector.AddTransition(featureId, segmentIdx, true /* oneWay */, true /* forwardIsEnter */, + {} /* backPoint */, {} /* frontPoint */); + + TestConnectorConsistency(connector); + TEST_EQUAL(connector.GetEnters().size(), 1, ()); + TEST_EQUAL(connector.GetExits().size(), 0, ()); + TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), + true /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), + false /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), + true /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), + false /* isOutgoing */), + ()); +} + +UNIT_TEST(OneWayExit) +{ + uint32_t constexpr featureId = 1; + uint32_t constexpr segmentIdx = 1; + CrossMwmConnector connector(mwmId); + connector.AddTransition(featureId, segmentIdx, true /* oneWay */, false /* forwardIsEnter */, + {} /* backPoint */, {} /* frontPoint */); + + TestConnectorConsistency(connector); + TEST_EQUAL(connector.GetEnters().size(), 0, ()); + TEST_EQUAL(connector.GetExits().size(), 1, ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), + true /* isOutgoing */), + ()); + TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), + false /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), + true /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), + false /* isOutgoing */), + ()); +} + +UNIT_TEST(TwoWayEnter) +{ + uint32_t constexpr featureId = 1; + uint32_t constexpr segmentIdx = 1; + CrossMwmConnector connector(mwmId); + connector.AddTransition(featureId, segmentIdx, false /* oneWay */, true /* forwardIsEnter */, + {} /* backPoint */, {} /* frontPoint */); + + TestConnectorConsistency(connector); + TEST_EQUAL(connector.GetEnters().size(), 1, ()); + TEST_EQUAL(connector.GetExits().size(), 1, ()); + TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), + true /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), + false /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), + true /* isOutgoing */), + ()); + TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), + false /* isOutgoing */), + ()); +} + +UNIT_TEST(TwoWayExit) +{ + uint32_t constexpr featureId = 1; + uint32_t constexpr segmentIdx = 1; + CrossMwmConnector connector(mwmId); + connector.AddTransition(featureId, segmentIdx, false /* oneWay */, false /* forwardIsEnter */, + {} /* backPoint */, {} /* frontPoint */); + + TestConnectorConsistency(connector); + TEST_EQUAL(connector.GetEnters().size(), 1, ()); + TEST_EQUAL(connector.GetExits().size(), 1, ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), + true /* isOutgoing */), + ()); + TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), + false /* isOutgoing */), + ()); + TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), + true /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), + false /* isOutgoing */), + ()); +} + +UNIT_TEST(Serialization) +{ + float constexpr kEdgesWeight = 333; + + vector<uint8_t> buffer; + { + vector<CrossMwmConnectorSerializer::Transition> transitions = { + /* featureId, segmentIdx, roadMask, oneWayMask, forwardIsEnter, backPoint, frontPoint */ + {10, 1, kCarMask, kCarMask, true, m2::PointD(1.1, 1.2), m2::PointD(1.3, 1.4)}, + {20, 2, kCarMask, 0, true, m2::PointD(2.1, 2.2), m2::PointD(2.3, 2.4)}, + {30, 3, kPedestrianMask, kCarMask, true, m2::PointD(3.1, 3.2), m2::PointD(3.3, 3.4)}}; + + CrossMwmConnectorPerVehicleType connectors; + CrossMwmConnector & carConnector = connectors[static_cast<size_t>(VehicleType::Car)]; + for (auto const & transition : transitions) + CrossMwmConnectorSerializer::AddTransition(transition, kCarMask, carConnector); + + carConnector.FillWeights( + [](Segment const & enter, Segment const & exit) { return kEdgesWeight; }); + + serial::CodingParams const codingParams; + MemWriter<vector<uint8_t>> writer(buffer); + CrossMwmConnectorSerializer::Serialize(transitions, connectors, codingParams, writer); + } + + CrossMwmConnector connector(mwmId); + { + MemReader reader(buffer.data(), buffer.size()); + ReaderSource<MemReader> source(reader); + CrossMwmConnectorSerializer::DeserializeTransitions(VehicleType::Car, connector, source); + } + + TestConnectorConsistency(connector); + + TEST_EQUAL(connector.GetEnters().size(), 2, ()); + TEST_EQUAL(connector.GetExits().size(), 1, ()); + + TEST(!connector.IsTransition(Segment(mwmId, 0, 0, true), true /* isOutgoing */), ()); + + TEST(connector.IsTransition(Segment(mwmId, 10, 1, true /* forward */), true /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, 10, 1, true /* forward */), false /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, 10, 1, false /* forward */), true /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, 10, 1, false /* forward */), false /* isOutgoing */), + ()); + + TEST(connector.IsTransition(Segment(mwmId, 20, 2, true /* forward */), true /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, 20, 2, true /* forward */), false /* isOutgoing */), + ()); + TEST(!connector.IsTransition(Segment(mwmId, 20, 2, false /* forward */), true /* isOutgoing */), + ()); + TEST(connector.IsTransition(Segment(mwmId, 20, 2, false /* forward */), false /* isOutgoing */), + ()); + + TEST(!connector.IsTransition(Segment(mwmId, 30, 3, true /* forward */), true /* isOutgoing */), + ()); + + TEST(!connector.WeightsWereLoaded(), ()); + TEST(!connector.HasWeights(), ()); + + { + MemReader reader(buffer.data(), buffer.size()); + ReaderSource<MemReader> source(reader); + CrossMwmConnectorSerializer::DeserializeWeights(VehicleType::Car, connector, source); + } + TEST(connector.WeightsWereLoaded(), ()); + TEST(connector.HasWeights(), ()); + + double constexpr eps = 1e-6; + TEST(AlmostEqualAbs( + connector.GetPoint(Segment(mwmId, 20, 2, true /* forward */), true /* front */), + m2::PointD(2.3, 2.4), eps), + ()); + TEST(AlmostEqualAbs( + connector.GetPoint(Segment(mwmId, 20, 2, true /* forward */), false /* front */), + m2::PointD(2.1, 2.2), eps), + ()); + TEST(AlmostEqualAbs( + connector.GetPoint(Segment(mwmId, 20, 2, false /* forward */), true /* front */), + m2::PointD(2.1, 2.2), eps), + ()); + TEST(AlmostEqualAbs( + connector.GetPoint(Segment(mwmId, 20, 2, true /* forward */), true /* front */), + m2::PointD(2.3, 2.4), eps), + ()); + + TestEdges(connector, Segment(mwmId, 10, 1, true /* forward */), true /* isOutgoing */, + {{Segment(mwmId, 20, 2, false /* forward */), kEdgesWeight}}); + + TestEdges(connector, Segment(mwmId, 20, 2, true /* forward */), true /* isOutgoing */, + {{Segment(mwmId, 20, 2, false /* forward */), kEdgesWeight}}); + + TestEdges(connector, Segment(mwmId, 20, 2, false /* forward */), false /* isOutgoing */, + {{Segment(mwmId, 10, 1, true /* forward */), kEdgesWeight}, + {Segment(mwmId, 20, 2, true /* forward */), kEdgesWeight}}); +} +} // namespace routing_test diff --git a/routing/routing_tests/routing_tests.pro b/routing/routing_tests/routing_tests.pro index c144590a22..5ea3209ae3 100644 --- a/routing/routing_tests/routing_tests.pro +++ b/routing/routing_tests/routing_tests.pro @@ -26,6 +26,7 @@ SOURCES += \ astar_progress_test.cpp \ astar_router_test.cpp \ async_router_test.cpp \ + cross_mwm_connector_test.cpp \ cross_routing_tests.cpp \ cumulative_restriction_test.cpp \ followed_polyline_test.cpp \ diff --git a/routing/segment.hpp b/routing/segment.hpp index cbeb14d449..1729b22cb2 100644 --- a/routing/segment.hpp +++ b/routing/segment.hpp @@ -75,6 +75,11 @@ public: Segment const & GetTarget() const { return m_target; } double GetWeight() const { return m_weight; } + bool operator==(SegmentEdge const & edge) const + { + return m_target == edge.m_target && m_weight == edge.m_weight; + } + private: // Target is vertex going to for outgoing edges, vertex going from for ingoing edges. Segment m_target; @@ -88,4 +93,11 @@ inline string DebugPrint(Segment const & segment) << segment.GetSegmentIdx() << ", " << segment.IsForward() << ")"; return out.str(); } + +inline string DebugPrint(SegmentEdge const & edge) +{ + ostringstream out; + out << "Edge(" << DebugPrint(edge.GetTarget()) << ", " << edge.GetWeight() << ")"; + return out.str(); +} } // namespace routing diff --git a/search/nearby_points_sweeper.hpp b/search/nearby_points_sweeper.hpp index 3bf6db6e2b..cfea4f3013 100644 --- a/search/nearby_points_sweeper.hpp +++ b/search/nearby_points_sweeper.hpp @@ -3,6 +3,7 @@ #include "base/assert.hpp" #include "std/algorithm.hpp" +#include "std/cmath.hpp" #include "std/cstdint.hpp" #include "std/limits.hpp" #include "std/set.hpp" diff --git a/search/search_quality/assessment_tool/CMakeLists.txt b/search/search_quality/assessment_tool/CMakeLists.txt index 3509cf68d5..cf02271f0a 100644 --- a/search/search_quality/assessment_tool/CMakeLists.txt +++ b/search/search_quality/assessment_tool/CMakeLists.txt @@ -8,13 +8,21 @@ include_directories(${OMIM_ROOT}/3party/glm) set( SRC - assessment_tool.cc + assessment_tool.cpp + helpers.cpp + helpers.hpp + languages_list.cpp + languages_list.hpp main_model.cpp main_model.hpp main_model.hpp main_view.cpp main_view.hpp model.hpp + sample_view.cpp + sample_view.hpp + samples_view.cpp + samples_view.hpp view.hpp ) diff --git a/search/search_quality/assessment_tool/assessment_tool.cc b/search/search_quality/assessment_tool/assessment_tool.cpp index ca43dd37e8..8967b845b1 100644 --- a/search/search_quality/assessment_tool/assessment_tool.cc +++ b/search/search_quality/assessment_tool/assessment_tool.cpp @@ -14,10 +14,6 @@ DEFINE_string(resources_path, "", "Path to resources directory"); DEFINE_string(data_path, "", "Path to data directory"); -namespace -{ -} // namespace - int main(int argc, char ** argv) { search::ChangeMaxNumberOfOpenFiles(search::kMaxOpenFiles); @@ -36,7 +32,7 @@ int main(int argc, char ** argv) Framework framework; MainView view(framework); - MainModel model; + MainModel model(framework); model.SetView(view); view.SetModel(model); diff --git a/search/search_quality/assessment_tool/helpers.cpp b/search/search_quality/assessment_tool/helpers.cpp new file mode 100644 index 0000000000..fccfdc363a --- /dev/null +++ b/search/search_quality/assessment_tool/helpers.cpp @@ -0,0 +1,13 @@ +#include "search/search_quality/assessment_tool/helpers.hpp" + +#include "base/string_utils.hpp" + +QString ToQString(strings::UniString const & s) +{ + return QString::fromUtf8(strings::ToUtf8(s).c_str()); +} + +QString ToQString(std::string const & s) +{ + return QString::fromUtf8(s.c_str()); +} diff --git a/search/search_quality/assessment_tool/helpers.hpp b/search/search_quality/assessment_tool/helpers.hpp new file mode 100644 index 0000000000..f72b873f1a --- /dev/null +++ b/search/search_quality/assessment_tool/helpers.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include <string> +#include <utility> + +#include <QtCore/QString> + +namespace strings +{ +class UniString; +} + +QString ToQString(strings::UniString const & s); +QString ToQString(std::string const & s); + +template <typename Layout, typename... Args> +Layout * BuildLayoutWithoutMargins(Args &&... args) +{ + auto * layout = new Layout(std::forward<Args>(args)...); + layout->setContentsMargins(0 /* left */, 0 /* top */, 0 /* right */, 0 /* bottom */); + return layout; +} diff --git a/search/search_quality/assessment_tool/languages_list.cpp b/search/search_quality/assessment_tool/languages_list.cpp new file mode 100644 index 0000000000..73e39d88ce --- /dev/null +++ b/search/search_quality/assessment_tool/languages_list.cpp @@ -0,0 +1,21 @@ +#include "search/search_quality/assessment_tool/languages_list.hpp" + +#include "search/search_quality/assessment_tool/helpers.hpp" + +#include "coding/multilang_utf8_string.hpp" + +LanguagesList::LanguagesList(QWidget * parent) : QComboBox(parent) +{ + auto const langs = StringUtf8Multilang::GetSupportedLanguages(); + for (auto const & lang : langs) + addItem(ToQString(lang.m_code)); +} + +void LanguagesList::Select(std::string const & lang) +{ + auto const index = StringUtf8Multilang::GetLangIndex(lang); + if (index != StringUtf8Multilang::kUnsupportedLanguageCode) + setCurrentIndex(index); + else + setCurrentIndex(StringUtf8Multilang::kDefaultCode); +} diff --git a/search/search_quality/assessment_tool/languages_list.hpp b/search/search_quality/assessment_tool/languages_list.hpp new file mode 100644 index 0000000000..c717c0aff4 --- /dev/null +++ b/search/search_quality/assessment_tool/languages_list.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include <string> + +#include <QtWidgets/QComboBox> + +class QWidget; + +class LanguagesList : public QComboBox +{ +public: + explicit LanguagesList(QWidget * parent); + + void Select(std::string const & lang); +}; diff --git a/search/search_quality/assessment_tool/main_model.cpp b/search/search_quality/assessment_tool/main_model.cpp index 73b5175f30..c41ecb4c80 100644 --- a/search/search_quality/assessment_tool/main_model.cpp +++ b/search/search_quality/assessment_tool/main_model.cpp @@ -2,6 +2,16 @@ #include "search/search_quality/assessment_tool/view.hpp" +#include "map/framework.hpp" + +#include "search/search_params.hpp" + +#include "geometry/mercator.hpp" + +#include "coding/multilang_utf8_string.hpp" + +#include "platform/platform.hpp" + #include "base/assert.hpp" #include "base/logging.hpp" @@ -9,6 +19,8 @@ #include <fstream> #include <iterator> +MainModel::MainModel(Framework & framework) : m_framework(framework) {} + void MainModel::Open(std::string const & path) { CHECK(m_view, ()); @@ -38,5 +50,38 @@ void MainModel::OnSampleSelected(int index) CHECK_GREATER_OR_EQUAL(index, 0, ()); CHECK_LESS(index, m_samples.size(), ()); CHECK(m_view, ()); + + auto const & sample = m_samples[index]; m_view->ShowSample(m_samples[index]); + + auto & engine = m_framework.GetSearchEngine(); + { + auto latLon = MercatorBounds::ToLatLon(sample.m_pos); + + search::SearchParams params; + params.m_query = strings::ToUtf8(sample.m_query); + params.m_inputLocale = sample.m_locale; + params.m_suggestsEnabled = false; + params.SetPosition(latLon.lat, latLon.lon); + + auto const timestamp = ++m_queryTimestamp; + m_numShownResults = 0; + + params.m_onResults = [this, timestamp](search::Results const & results) { + GetPlatform().RunOnGuiThread([this, timestamp, results]() { OnResults(timestamp, results); }); + }; + + if (auto handle = m_queryHandle.lock()) + handle->Cancel(); + m_queryHandle = engine.Search(params, sample.m_viewport); + } +} + +void MainModel::OnResults(uint64_t timestamp, search::Results const & results) +{ + if (timestamp != m_queryTimestamp) + return; + CHECK_LESS_OR_EQUAL(m_numShownResults, results.GetCount(), ()); + m_view->ShowResults(results.begin() + m_numShownResults, results.end()); + m_numShownResults = results.GetCount(); } diff --git a/search/search_quality/assessment_tool/main_model.hpp b/search/search_quality/assessment_tool/main_model.hpp index fbbcced766..868c9a5772 100644 --- a/search/search_quality/assessment_tool/main_model.hpp +++ b/search/search_quality/assessment_tool/main_model.hpp @@ -1,19 +1,36 @@ #pragma once +#include "search/engine.hpp" #include "search/search_quality/assessment_tool/model.hpp" #include "search/search_quality/sample.hpp" +#include <cstdint> #include <vector> +#include <memory> class Framework; +namespace search +{ +class Results; +} + class MainModel : public Model { public: + explicit MainModel(Framework & framework); + // Model overrides: void Open(std::string const & path) override; void OnSampleSelected(int index) override; private: + void OnResults(uint64_t timestamp, search::Results const & results); + + Framework & m_framework; std::vector<search::Sample> m_samples; + + std::weak_ptr<search::ProcessorHandle> m_queryHandle; + uint64_t m_queryTimestamp = 0; + size_t m_numShownResults = 0; }; diff --git a/search/search_quality/assessment_tool/main_view.cpp b/search/search_quality/assessment_tool/main_view.cpp index 5ced97c8b2..b0d276237d 100644 --- a/search/search_quality/assessment_tool/main_view.cpp +++ b/search/search_quality/assessment_tool/main_view.cpp @@ -1,6 +1,9 @@ #include "search/search_quality/assessment_tool/main_view.hpp" -#include "search/search_quality/assessment_tool/main_model.hpp" +#include "search/search_quality/assessment_tool/helpers.hpp" +#include "search/search_quality/assessment_tool/model.hpp" +#include "search/search_quality/assessment_tool/sample_view.hpp" +#include "search/search_quality/assessment_tool/samples_view.hpp" #include "qt/qt_common/map_widget.hpp" #include "qt/qt_common/scale_slider.hpp" @@ -8,14 +11,12 @@ #include "map/framework.hpp" #include "base/assert.hpp" -#include "base/stl_add.hpp" #include "base/string_utils.hpp" -#include <QtCore/QList> #include <QtCore/Qt> +#include <QtWidgets/QDockWidget> #include <QtWidgets/QFileDialog> #include <QtWidgets/QHBoxLayout> -#include <QtWidgets/QHeaderView> #include <QtWidgets/QMenuBar> #include <QtWidgets/QMessageBox> #include <QtWidgets/QToolBar> @@ -39,17 +40,20 @@ MainView::~MainView() void MainView::SetSamples(std::vector<search::Sample> const & samples) { - m_samplesModel->removeRows(0, m_samplesModel->rowCount()); - for (auto const & sample : samples) - { - m_samplesModel->appendRow( - new QStandardItem(QString::fromUtf8(strings::ToUtf8(sample.m_query).c_str()))); - } + m_samplesView->SetSamples(samples); } void MainView::ShowSample(search::Sample const & sample) { - // TODO (@y): implement a dock view for search sample. + m_framework.ShowRect(sample.m_viewport, -1 /* maxScale */, false /* animation */); + + m_sampleView->SetContents(sample); + m_sampleView->show(); +} + +void MainView::ShowResults(search::Results::Iter begin, search::Results::Iter end) +{ + m_sampleView->ShowResults(begin, end); } void MainView::ShowError(std::string const & msg) @@ -70,84 +74,77 @@ void MainView::OnSampleSelected(QItemSelection const & current) void MainView::InitMenuBar() { - CHECK(m_samplesDock.get(), ()); - auto * bar = menuBar(); auto * fileMenu = bar->addMenu(tr("&File")); { - auto open = make_unique<QAction>(tr("&Open queries..."), this); + auto * open = new QAction(tr("&Open queries..."), this /* parent */); open->setShortcuts(QKeySequence::Open); open->setStatusTip(tr("Open the file with queries for assessment")); - connect(open.get(), &QAction::triggered, this, &MainView::Open); - fileMenu->addAction(open.release()); + connect(open, &QAction::triggered, this, &MainView::Open); + fileMenu->addAction(open); } fileMenu->addSeparator(); { - auto quit = make_unique<QAction>(tr("&Quit"), this); + auto * quit = new QAction(tr("&Quit"), this /* parent */); quit->setShortcuts(QKeySequence::Quit); quit->setStatusTip(tr("Exit the tool")); - connect(quit.get(), &QAction::triggered, this, &QWidget::close); - fileMenu->addAction(quit.release()); + connect(quit, &QAction::triggered, this, &QWidget::close); + fileMenu->addAction(quit); } auto * viewMenu = bar->addMenu(tr("&View")); + { + CHECK(m_samplesDock != nullptr, ()); viewMenu->addAction(m_samplesDock->toggleViewAction()); } + + { + CHECK(m_sampleDock != nullptr, ()); + viewMenu->addAction(m_sampleDock->toggleViewAction()); + } } void MainView::InitMapWidget() { - auto widget = make_unique<QWidget>(this /* parent */); + auto * widget = new QWidget(this /* parent */); + auto * layout = BuildLayoutWithoutMargins<QHBoxLayout>(widget /* parent */); + widget->setLayout(layout); - auto layout = make_unique<QHBoxLayout>(widget.get() /* parent */); - layout->setContentsMargins(0 /* left */, 0 /* top */, 0 /* right */, 0 /* bottom */); { - auto mapWidget = make_unique<qt::common::MapWidget>(m_framework, widget.get() /* parent */); - auto toolBar = make_unique<QToolBar>(widget.get() /* parent */); + auto * mapWidget = new qt::common::MapWidget(m_framework, widget /* parent */); + auto * toolBar = new QToolBar(widget /* parent */); toolBar->setOrientation(Qt::Vertical); toolBar->setIconSize(QSize(32, 32)); qt::common::ScaleSlider::Embed(Qt::Vertical, *toolBar, *mapWidget); - layout->addWidget(mapWidget.release()); - layout->addWidget(toolBar.release()); + layout->addWidget(mapWidget); + layout->addWidget(toolBar); } - widget->setLayout(layout.release()); - setCentralWidget(widget.release()); + setCentralWidget(widget); } void MainView::InitDocks() { - m_samplesTable = my::make_unique<QTableView>(this /* parent */); - m_samplesTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_samplesTable->setSelectionMode(QAbstractItemView::SingleSelection); - - { - auto * header = m_samplesTable->horizontalHeader(); - header->setStretchLastSection(true /* stretch */); - header->hide(); - } - - m_samplesModel = - my::make_unique<QStandardItemModel>(0 /* rows */, 1 /* columns */, this /* parent */); - - m_samplesTable->setModel(m_samplesModel.get()); + m_samplesView = new SamplesView(this /* parent */); { - auto * model = m_samplesTable->selectionModel(); + auto * model = m_samplesView->selectionModel(); connect(model, SIGNAL(selectionChanged(QItemSelection const &, QItemSelection const &)), this, SLOT(OnSampleSelected(QItemSelection const &))); } - m_samplesDock = my::make_unique<QDockWidget>(tr("Samples"), this /* parent */, Qt::Widget); - m_samplesDock->setFeatures(QDockWidget::AllDockWidgetFeatures); - m_samplesDock->setWidget(m_samplesTable.get()); - addDockWidget(Qt::RightDockWidgetArea, m_samplesDock.get()); + m_samplesDock = CreateDock("Samples", *m_samplesView); + addDockWidget(Qt::RightDockWidgetArea, m_samplesDock); + + m_sampleView = new SampleView(this /* parent */); + m_sampleDock = CreateDock("Sample", *m_sampleView); + addDockWidget(Qt::RightDockWidgetArea, m_sampleDock); } void MainView::Open() @@ -162,3 +159,11 @@ void MainView::Open() m_model->Open(file); } + +QDockWidget * MainView::CreateDock(std::string const & title, QWidget & widget) +{ + auto * dock = new QDockWidget(ToQString(title), this /* parent */, Qt::Widget); + dock->setFeatures(QDockWidget::AllDockWidgetFeatures); + dock->setWidget(&widget); + return dock; +} diff --git a/search/search_quality/assessment_tool/main_view.hpp b/search/search_quality/assessment_tool/main_view.hpp index dcde4ecaeb..beec69483a 100644 --- a/search/search_quality/assessment_tool/main_view.hpp +++ b/search/search_quality/assessment_tool/main_view.hpp @@ -2,14 +2,13 @@ #include "search/search_quality/assessment_tool/view.hpp" -#include <QtGui/QStandardItemModel> -#include <QtWidgets/QDockWidget> #include <QtWidgets/QMainWindow> -#include <QtWidgets/QTableView> - -#include <memory> +class Framework; +class QDockWidget; class QItemSelection; +class SamplesView; +class SampleView; namespace qt { @@ -19,8 +18,6 @@ class MapWidget; } } -class Framework; - class MainView : public QMainWindow, public View { Q_OBJECT @@ -32,21 +29,26 @@ public: // View overrides: void SetSamples(std::vector<search::Sample> const & samples) override; void ShowSample(search::Sample const & sample) override; + void ShowResults(search::Results::Iter begin, search::Results::Iter end) override; void ShowError(std::string const & msg) override; private Q_SLOTS: void OnSampleSelected(QItemSelection const & current); private: - void InitMenuBar(); void InitMapWidget(); void InitDocks(); + void InitMenuBar(); void Open(); + QDockWidget * CreateDock(std::string const & title, QWidget & widget); + Framework & m_framework; - std::unique_ptr<QStandardItemModel> m_samplesModel; - std::unique_ptr<QTableView> m_samplesTable; - std::unique_ptr<QDockWidget> m_samplesDock; + SamplesView * m_samplesView = nullptr; + QDockWidget * m_samplesDock = nullptr; + + SampleView * m_sampleView = nullptr; + QDockWidget * m_sampleDock = nullptr; }; diff --git a/search/search_quality/assessment_tool/sample_view.cpp b/search/search_quality/assessment_tool/sample_view.cpp new file mode 100644 index 0000000000..cbf37f7bf4 --- /dev/null +++ b/search/search_quality/assessment_tool/sample_view.cpp @@ -0,0 +1,106 @@ +#include "search/search_quality/assessment_tool/sample_view.hpp" + +#include "search/result.hpp" +#include "search/search_quality/assessment_tool/helpers.hpp" +#include "search/search_quality/assessment_tool/languages_list.hpp" +#include "search/search_quality/sample.hpp" + +#include <QtGui/QStandardItem> +#include <QtGui/QStandardItemModel> +#include <QtWidgets/QComboBox> +#include <QtWidgets/QGroupBox> +#include <QtWidgets/QHBoxLayout> +#include <QtWidgets/QHeaderView> +#include <QtWidgets/QLabel> +#include <QtWidgets/QLineEdit> +#include <QtWidgets/QListWidget> +#include <QtWidgets/QVBoxLayout> + +namespace +{ +QWidget * CreateSampleWidget(QWidget & parent, search::Result const & result) +{ + auto * widget = new QWidget(&parent); + auto * layout = new QVBoxLayout(widget); + widget->setLayout(layout); + + layout->addWidget(new QLabel(ToQString(result.GetString()))); + + if (result.GetResultType() == search::Result::RESULT_FEATURE && !result.GetFeatureType().empty()) + { + auto * type = new QLabel(ToQString(result.GetFeatureType()), widget); + type->setStyleSheet("QLabel { font-size : 8pt }"); + layout->addWidget(type); + } + + if (!result.GetAddress().empty()) + { + auto * address = new QLabel(ToQString(result.GetAddress()), widget); + address->setStyleSheet("QLabel { color : green ; font-size : 8pt }"); + layout->addWidget(address); + } + + return widget; +} +} // namespace + +SampleView::SampleView(QWidget * parent) : QWidget(parent) +{ + auto * mainLayout = BuildLayoutWithoutMargins<QVBoxLayout>(this /* parent */); + + { + auto * box = new QWidget(this /* parent */); + auto * layout = BuildLayoutWithoutMargins<QHBoxLayout>(box /* parent */); + box->setLayout(layout); + + m_query = new QLineEdit(this /* parent */); + m_query->setToolTip(tr("Query text")); + layout->addWidget(m_query); + + m_langs = new LanguagesList(this /* parent */); + m_langs->setToolTip(tr("Query input language")); + layout->addWidget(m_langs); + + mainLayout->addWidget(box); + } + + { + auto * box = new QWidget(this /* parent */); + auto * layout = BuildLayoutWithoutMargins<QVBoxLayout>(box /* parent */); + box->setLayout(layout); + + layout->addWidget(new QLabel(tr("Found results"))); + + m_results = new QListWidget(box /* parent */); + layout->addWidget(m_results); + + mainLayout->addWidget(box); + } + + setLayout(mainLayout); + setWindowTitle(tr("Sample")); +} + +void SampleView::SetContents(search::Sample const & sample) +{ + m_query->setText(ToQString(sample.m_query)); + m_query->home(false /* mark */); + + m_langs->Select(sample.m_locale); + + m_results->clear(); +} + +void SampleView::ShowResults(search::Results::Iter begin, search::Results::Iter end) +{ + for (auto it = begin; it != end; ++it) + { + auto * item = new QListWidgetItem(m_results); + m_results->addItem(item); + + auto * widget = CreateSampleWidget(*m_results /* parent */, *it /* sample */); + item->setSizeHint(widget->minimumSizeHint()); + + m_results->setItemWidget(item, widget); + } +} diff --git a/search/search_quality/assessment_tool/sample_view.hpp b/search/search_quality/assessment_tool/sample_view.hpp new file mode 100644 index 0000000000..ed509bc87c --- /dev/null +++ b/search/search_quality/assessment_tool/sample_view.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "search/result.hpp" + +#include <QtWidgets/QWidget> + +class LanguagesList; +class QLineEdit; +class QListWidget; + +namespace search +{ +struct Sample; +} + +class SampleView : public QWidget +{ +public: + explicit SampleView(QWidget * parent); + + void SetContents(search::Sample const & sample); + void ShowResults(search::Results::Iter begin, search::Results::Iter end); + +private: + QLineEdit * m_query = nullptr; + LanguagesList * m_langs = nullptr; + QListWidget * m_results = nullptr; +}; diff --git a/search/search_quality/assessment_tool/samples_view.cpp b/search/search_quality/assessment_tool/samples_view.cpp new file mode 100644 index 0000000000..4e51010fae --- /dev/null +++ b/search/search_quality/assessment_tool/samples_view.cpp @@ -0,0 +1,29 @@ +#include "search/search_quality/assessment_tool/samples_view.hpp" + +#include "search/search_quality/assessment_tool/helpers.hpp" + +#include <QtGui/QStandardItem> +#include <QtGui/QStandardItemModel> +#include <QtWidgets/QHeaderView> + +SamplesView::SamplesView(QWidget * parent) : QTableView(parent) +{ + setEditTriggers(QAbstractItemView::NoEditTriggers); + setSelectionMode(QAbstractItemView::SingleSelection); + + { + auto * header = horizontalHeader(); + header->setStretchLastSection(true /* stretch */); + header->hide(); + } + + m_model = new QStandardItemModel(0 /* rows */, 1 /* columns */, this /* parent */); + setModel(m_model); +} + +void SamplesView::SetSamples(std::vector<search::Sample> const & samples) +{ + m_model->removeRows(0, m_model->rowCount()); + for (auto const & sample : samples) + m_model->appendRow(new QStandardItem(ToQString(sample.m_query))); +} diff --git a/search/search_quality/assessment_tool/samples_view.hpp b/search/search_quality/assessment_tool/samples_view.hpp new file mode 100644 index 0000000000..43fd0860e1 --- /dev/null +++ b/search/search_quality/assessment_tool/samples_view.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "search/search_quality/sample.hpp" + +#include <vector> + +#include <QtWidgets/QTableView> + +class QStandardItemModel; + +class SamplesView : public QTableView +{ +public: + explicit SamplesView(QWidget * parent); + + void SetSamples(std::vector<search::Sample> const & samples); + +private: + QStandardItemModel * m_model = nullptr; +}; diff --git a/search/search_quality/assessment_tool/view.hpp b/search/search_quality/assessment_tool/view.hpp index 7f7a65adfd..f0643c12ee 100644 --- a/search/search_quality/assessment_tool/view.hpp +++ b/search/search_quality/assessment_tool/view.hpp @@ -1,5 +1,6 @@ #pragma once +#include "search/result.hpp" #include "search/search_quality/sample.hpp" #include <string> @@ -16,6 +17,7 @@ public: virtual void SetSamples(std::vector<search::Sample> const & samples) = 0; virtual void ShowSample(search::Sample const & sample) = 0; + virtual void ShowResults(search::Results::Iter begin, search::Results::Iter end) = 0; virtual void ShowError(std::string const & msg) = 0; protected: diff --git a/xcode/routing/routing.xcodeproj/project.pbxproj b/xcode/routing/routing.xcodeproj/project.pbxproj index 4810e0ec8e..e7d736ca87 100644 --- a/xcode/routing/routing.xcodeproj/project.pbxproj +++ b/xcode/routing/routing.xcodeproj/project.pbxproj @@ -25,6 +25,11 @@ 0C5992E21E433BE600203653 /* num_mwm_id.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C5992E11E433BE600203653 /* num_mwm_id.hpp */; }; 0C5BC9D11E28FD4E0071BFDD /* index_road_graph.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0C5BC9CF1E28FD4E0071BFDD /* index_road_graph.cpp */; }; 0C5BC9D21E28FD4E0071BFDD /* index_road_graph.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C5BC9D01E28FD4E0071BFDD /* index_road_graph.hpp */; }; + 0C5F5D201E798B0400307B98 /* cross_mwm_connector_serialization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0C5F5D1C1E798B0400307B98 /* cross_mwm_connector_serialization.cpp */; }; + 0C5F5D211E798B0400307B98 /* cross_mwm_connector_serialization.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C5F5D1D1E798B0400307B98 /* cross_mwm_connector_serialization.hpp */; }; + 0C5F5D221E798B0400307B98 /* cross_mwm_connector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0C5F5D1E1E798B0400307B98 /* cross_mwm_connector.cpp */; }; + 0C5F5D231E798B0400307B98 /* cross_mwm_connector.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C5F5D1F1E798B0400307B98 /* cross_mwm_connector.hpp */; }; + 0C5F5D251E798B3800307B98 /* cross_mwm_connector_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0C5F5D241E798B3800307B98 /* cross_mwm_connector_test.cpp */; }; 0C5FEC541DDE191E0017688C /* edge_estimator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0C5FEC521DDE191E0017688C /* edge_estimator.cpp */; }; 0C5FEC551DDE191E0017688C /* edge_estimator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C5FEC531DDE191E0017688C /* edge_estimator.hpp */; }; 0C5FEC5E1DDE192A0017688C /* geometry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0C5FEC561DDE192A0017688C /* geometry.cpp */; }; @@ -271,6 +276,11 @@ 0C5992E11E433BE600203653 /* num_mwm_id.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = num_mwm_id.hpp; sourceTree = "<group>"; }; 0C5BC9CF1E28FD4E0071BFDD /* index_road_graph.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = index_road_graph.cpp; sourceTree = "<group>"; }; 0C5BC9D01E28FD4E0071BFDD /* index_road_graph.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = index_road_graph.hpp; sourceTree = "<group>"; }; + 0C5F5D1C1E798B0400307B98 /* cross_mwm_connector_serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cross_mwm_connector_serialization.cpp; sourceTree = "<group>"; }; + 0C5F5D1D1E798B0400307B98 /* cross_mwm_connector_serialization.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cross_mwm_connector_serialization.hpp; sourceTree = "<group>"; }; + 0C5F5D1E1E798B0400307B98 /* cross_mwm_connector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cross_mwm_connector.cpp; sourceTree = "<group>"; }; + 0C5F5D1F1E798B0400307B98 /* cross_mwm_connector.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cross_mwm_connector.hpp; sourceTree = "<group>"; }; + 0C5F5D241E798B3800307B98 /* cross_mwm_connector_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cross_mwm_connector_test.cpp; sourceTree = "<group>"; }; 0C5FEC521DDE191E0017688C /* edge_estimator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = edge_estimator.cpp; sourceTree = "<group>"; }; 0C5FEC531DDE191E0017688C /* edge_estimator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = edge_estimator.hpp; sourceTree = "<group>"; }; 0C5FEC561DDE192A0017688C /* geometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = geometry.cpp; sourceTree = "<group>"; }; @@ -579,6 +589,7 @@ 6742ACA71C68A0B1009CB89E /* astar_progress_test.cpp */, 6742ACA81C68A0B1009CB89E /* astar_router_test.cpp */, 6742ACA91C68A0B1009CB89E /* async_router_test.cpp */, + 0C5F5D241E798B3800307B98 /* cross_mwm_connector_test.cpp */, 6742ACAA1C68A0B1009CB89E /* cross_routing_tests.cpp */, 6742ACAB1C68A0B1009CB89E /* followed_polyline_test.cpp */, 0C5FEC6C1DDE19A40017688C /* index_graph_test.cpp */, @@ -704,6 +715,10 @@ 56099E311CC9247E00A7772A /* bicycle_directions.hpp */, 56826BCE1DB51C4E00807C62 /* car_router.cpp */, 56826BCF1DB51C4E00807C62 /* car_router.hpp */, + 0C5F5D1E1E798B0400307B98 /* cross_mwm_connector.cpp */, + 0C5F5D1F1E798B0400307B98 /* cross_mwm_connector.hpp */, + 0C5F5D1C1E798B0400307B98 /* cross_mwm_connector_serialization.cpp */, + 0C5F5D1D1E798B0400307B98 /* cross_mwm_connector_serialization.hpp */, A120B3411B4A7BE5002F3808 /* cross_mwm_road_graph.cpp */, A120B3421B4A7BE5002F3808 /* cross_mwm_road_graph.hpp */, A120B3431B4A7BE5002F3808 /* cross_mwm_router.cpp */, @@ -845,6 +860,7 @@ 56099E2A1CC7C97D00A7772A /* routing_result_graph.hpp in Headers */, A120B3481B4A7BE5002F3808 /* cross_mwm_router.hpp in Headers */, 674F9BD31B0A580E00704FFA /* road_graph_router.hpp in Headers */, + 0C5F5D231E798B0400307B98 /* cross_mwm_connector.hpp in Headers */, 675344141A3F644F00A0A8C3 /* osrm_data_facade.hpp in Headers */, 6753441F1A3F644F00A0A8C3 /* turns.hpp in Headers */, 0C5FEC611DDE192A0017688C /* index_graph.hpp in Headers */, @@ -875,6 +891,7 @@ 0C8705051E0182F200BCAF71 /* route_point.hpp in Headers */, A1876BC71BB19C4300C9C743 /* speed_camera.hpp in Headers */, 56EA2FD51D8FD8590083F01A /* routing_helpers.hpp in Headers */, + 0C5F5D211E798B0400307B98 /* cross_mwm_connector_serialization.hpp in Headers */, 0C0DF9221DE898B70055A22F /* index_graph_starter.hpp in Headers */, 0C090C881E4E276700D52AFD /* world_graph.hpp in Headers */, 56099E2F1CC8FBDA00A7772A /* osrm_path_segment_factory.hpp in Headers */, @@ -1106,6 +1123,7 @@ files = ( 0C5FEC641DDE192A0017688C /* joint.cpp in Sources */, 0C090C871E4E276700D52AFD /* world_graph.cpp in Sources */, + 0C5F5D201E798B0400307B98 /* cross_mwm_connector_serialization.cpp in Sources */, 56CA09E71E30E73B00D05C9A /* restriction_test.cpp in Sources */, 0C5BC9D11E28FD4E0071BFDD /* index_road_graph.cpp in Sources */, 56826BD01DB51C4E00807C62 /* car_router.cpp in Sources */, @@ -1121,8 +1139,10 @@ 670EE5731B664796001E8064 /* pedestrian_directions.cpp in Sources */, 6753441B1A3F644F00A0A8C3 /* route.cpp in Sources */, 674F9BCA1B0A580E00704FFA /* async_router.cpp in Sources */, + 0C5F5D221E798B0400307B98 /* cross_mwm_connector.cpp in Sources */, 675344191A3F644F00A0A8C3 /* osrm2feature_map.cpp in Sources */, 670D049E1B0B4A970013A7AC /* nearest_edge_finder.cpp in Sources */, + 0C5F5D251E798B3800307B98 /* cross_mwm_connector_test.cpp in Sources */, 674F9BD61B0A580E00704FFA /* turns_generator.cpp in Sources */, A17B42981BCFBD0E00A1EAE4 /* osrm_helpers.cpp in Sources */, 674F9BD21B0A580E00704FFA /* road_graph_router.cpp in Sources */, |