diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b70abae3b..7d0ae30b37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -340,6 +340,7 @@ if (PLATFORM_DESKTOP) add_subdirectory(openlr) add_subdirectory(generator) add_subdirectory(skin_generator) + add_subdirectory(track_analyzing) endif() omim_add_test_subdirectory(qt_tstfrm) diff --git a/base/timer.cpp b/base/timer.cpp index 56b0d63811..f4dcc1a28a 100644 --- a/base/timer.cpp +++ b/base/timer.cpp @@ -84,6 +84,11 @@ std::string TimestampToString(time_t time) return buf; } +std::string SecondsSinceEpochToString(uint64_t secondsSinceEpoch) +{ + return TimestampToString(SecondsSinceEpochToTimeT(secondsSinceEpoch)); +} + namespace { bool IsValid(tm const & t) diff --git a/base/timer.hpp b/base/timer.hpp index 72992b523d..36d2599f07 100644 --- a/base/timer.hpp +++ b/base/timer.hpp @@ -48,6 +48,8 @@ uint64_t SecondsSinceEpoch(); /// Returns empty string on error std::string TimestampToString(time_t time); +std::string SecondsSinceEpochToString(uint64_t secondsSinceEpoch); + time_t const INVALID_TIME_STAMP = -1; /// Accepts strings in UTC format: 1997-07-16T07:30:15Z diff --git a/generator/routing_index_generator.cpp b/generator/routing_index_generator.cpp index a96df99732..77a66cfea2 100644 --- a/generator/routing_index_generator.cpp +++ b/generator/routing_index_generator.cpp @@ -165,17 +165,6 @@ private: IndexGraph & m_graph; }; -bool RegionsContain(vector const & regions, m2::PointD const & point) -{ - for (auto const & region : regions) - { - if (region.Contains(point)) - return true; - } - - return false; -} - // Calculate distance from the starting border point to the transition along the border. // It could be measured clockwise or counterclockwise, direction doesn't matter. RouteWeight CalcDistanceAlongTheBorders(vector const & borders, @@ -236,11 +225,11 @@ void CalcCrossMwmTransitions(string const & path, string const & mwmFile, string CHECK(osmIt != featureIdToOsmId.end(), ("Can't find osm id for feature id", featureId)); uint64_t const osmId = osmIt->second.OsmId(); - bool prevPointIn = RegionsContain(borders, f.GetPoint(0)); + bool prevPointIn = m2::RegionsContain(borders, f.GetPoint(0)); for (size_t i = 1; i < pointsCount; ++i) { - bool const currPointIn = RegionsContain(borders, f.GetPoint(i)); + bool const currPointIn = m2::RegionsContain(borders, f.GetPoint(i)); if (currPointIn == prevPointIn) continue; diff --git a/geometry/region2d.hpp b/geometry/region2d.hpp index b13832651f..2a86a3943c 100644 --- a/geometry/region2d.hpp +++ b/geometry/region2d.hpp @@ -344,6 +344,18 @@ namespace m2 return (DebugPrint(r.m_rect) + ::DebugPrint(r.m_points)); } + template + bool RegionsContain(vector> const & regions, PointT const & point) + { + for (auto const & region : regions) + { + if (region.Contains(point)) + return true; + } + + return false; + } + typedef Region RegionD; typedef Region RegionI; typedef Region RegionU; diff --git a/map/CMakeLists.txt b/map/CMakeLists.txt index 1325aebf6f..887258c910 100644 --- a/map/CMakeLists.txt +++ b/map/CMakeLists.txt @@ -50,14 +50,14 @@ set( local_ads_mark.cpp local_ads_mark.hpp local_ads_supported_types.cpp - mwm_tree.cpp - mwm_tree.hpp mwm_url.cpp mwm_url.hpp place_page_info.cpp place_page_info.hpp reachable_by_taxi_checker.cpp reachable_by_taxi_checker.hpp + routing_helpers.cpp + routing_helpers.hpp routing_manager.cpp routing_manager.hpp routing_mark.cpp diff --git a/map/framework.cpp b/map/framework.cpp index f2bd48cb65..1205a11672 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -5,7 +5,7 @@ #include "map/ge0_parser.hpp" #include "map/geourl_process.hpp" #include "map/gps_tracker.hpp" -#include "map/mwm_tree.hpp" +#include "map/routing_helpers.hpp" #include "map/taxi_delegate.hpp" #include "map/user_mark.hpp" diff --git a/map/map.pro b/map/map.pro index 58d2a73d19..16c595d57b 100644 --- a/map/map.pro +++ b/map/map.pro @@ -29,10 +29,10 @@ HEADERS += \ gps_tracker.hpp \ local_ads_manager.hpp \ local_ads_mark.hpp \ - mwm_tree.hpp \ mwm_url.hpp \ place_page_info.hpp \ reachable_by_taxi_checker.hpp \ + routing_helpers.cpp \ routing_manager.hpp \ routing_mark.hpp \ taxi_delegate.hpp \ @@ -63,10 +63,10 @@ SOURCES += \ local_ads_manager.cpp \ local_ads_mark.cpp \ local_ads_supported_types.cpp \ - mwm_tree.cpp \ mwm_url.cpp \ place_page_info.cpp \ reachable_by_taxi_checker.cpp \ + routing_helpers.cpp \ routing_manager.cpp \ routing_mark.cpp \ taxi_delegate.cpp \ diff --git a/map/mwm_tree.cpp b/map/mwm_tree.cpp deleted file mode 100644 index 2241c26aaa..0000000000 --- a/map/mwm_tree.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "map/mwm_tree.hpp" - -std::unique_ptr> MakeNumMwmTree(routing::NumMwmIds const & numMwmIds, - storage::CountryInfoGetter const & countryInfoGetter) -{ - auto tree = my::make_unique>(); - - numMwmIds.ForEachId([&](routing::NumMwmId numMwmId) { - auto const & countryName = numMwmIds.GetFile(numMwmId).GetName(); - tree->Add(numMwmId, countryInfoGetter.GetLimitRectForLeaf(countryName)); - }); - - return tree; -} diff --git a/map/routing_helpers.cpp b/map/routing_helpers.cpp new file mode 100644 index 0000000000..893ff13dc2 --- /dev/null +++ b/map/routing_helpers.cpp @@ -0,0 +1,22 @@ +#include "map/routing_helpers.hpp" + +std::unique_ptr> MakeNumMwmTree( + routing::NumMwmIds const & numMwmIds, storage::CountryInfoGetter const & countryInfoGetter) +{ + auto tree = my::make_unique>(); + + numMwmIds.ForEachId([&](routing::NumMwmId numMwmId) { + auto const & countryName = numMwmIds.GetFile(numMwmId).GetName(); + tree->Add(numMwmId, countryInfoGetter.GetLimitRectForLeaf(countryName)); + }); + + return tree; +} + +std::shared_ptr CreateNumMwmIds(storage::Storage const & storage) +{ + auto numMwmIds = std::make_shared(); + storage.ForEachCountryFile( + [&](platform::CountryFile const & file) { numMwmIds->RegisterFile(file); }); + return numMwmIds; +} diff --git a/map/routing_helpers.hpp b/map/routing_helpers.hpp new file mode 100644 index 0000000000..271218efb5 --- /dev/null +++ b/map/routing_helpers.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "routing/num_mwm_id.hpp" + +#include "storage/country_info_getter.hpp" +#include "storage/storage.hpp" + +#include "geometry/tree4d.hpp" + +#include + +std::unique_ptr> MakeNumMwmTree( + routing::NumMwmIds const & numMwmIds, storage::CountryInfoGetter const & countryInfoGetter); +std::shared_ptr CreateNumMwmIds(storage::Storage const & storage); diff --git a/routing/routing_integration_tests/routing_test_tools.cpp b/routing/routing_integration_tests/routing_test_tools.cpp index c535533457..caf665284b 100644 --- a/routing/routing_integration_tests/routing_test_tools.cpp +++ b/routing/routing_integration_tests/routing_test_tools.cpp @@ -5,7 +5,7 @@ #include "testing/testing.hpp" #include "map/feature_vec_model.hpp" -#include "map/mwm_tree.hpp" +#include "map/routing_helpers.hpp" #include "geometry/distance_on_sphere.hpp" #include "geometry/latlon.hpp" diff --git a/routing/segment.hpp b/routing/segment.hpp index e8001894e1..d220dee479 100644 --- a/routing/segment.hpp +++ b/routing/segment.hpp @@ -65,6 +65,12 @@ public: bool operator!=(Segment const & seg) const { return !(*this == seg); } + bool IsInverse(Segment const & seg) const + { + return m_featureId == seg.m_featureId && m_segmentIdx == seg.m_segmentIdx && + m_mwmId == seg.m_mwmId && m_forward != seg.m_forward; + } + private: uint32_t m_featureId = 0; uint32_t m_segmentIdx = 0; diff --git a/routing_common/vehicle_model.hpp b/routing_common/vehicle_model.hpp index 04b30db243..f7d8a2a58c 100644 --- a/routing_common/vehicle_model.hpp +++ b/routing_common/vehicle_model.hpp @@ -65,6 +65,12 @@ public: bool m_isTransitAllowed; /// transit allowed for this road type }; + struct AdditionalRoadTags final + { + std::initializer_list m_hwtag; + double m_speedKMpH; + }; + typedef std::initializer_list InitListT; VehicleModel(Classificator const & c, InitListT const & featureTypeLimits); @@ -92,12 +98,6 @@ public: } protected: - struct AdditionalRoadTags final - { - std::initializer_list m_hwtag; - double m_speedKMpH; - }; - /// @returns a special restriction which is set to the feature. virtual RoadAvailability GetRoadAvailability(feature::TypesHolder const & types) const; diff --git a/track_analyzing/CMakeLists.txt b/track_analyzing/CMakeLists.txt new file mode 100644 index 0000000000..6008a3ea25 --- /dev/null +++ b/track_analyzing/CMakeLists.txt @@ -0,0 +1,17 @@ +project(track_analyzing) + +set( + SRC + exceptions.hpp + log_parser.cpp + log_parser.hpp + serialization.hpp + track.hpp + track_matcher.cpp + track_matcher.hpp + utils.cpp + utils.hpp +) + +add_library(${PROJECT_NAME} ${SRC}) +add_subdirectory(track_analyzer) diff --git a/track_analyzing/exceptions.hpp b/track_analyzing/exceptions.hpp new file mode 100644 index 0000000000..a76337f841 --- /dev/null +++ b/track_analyzing/exceptions.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "base/exception.hpp" + +namespace tracking +{ +DECLARE_EXCEPTION(MessageException, RootException); +} // namespace tracking diff --git a/track_analyzing/log_parser.cpp b/track_analyzing/log_parser.cpp new file mode 100644 index 0000000000..c1dd61c7a6 --- /dev/null +++ b/track_analyzing/log_parser.cpp @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "track_analyzing/log_parser.hpp" +#include +#include + +using namespace std; +using namespace tracking; + +namespace +{ +vector ReadDataPoints(string const & data) +{ + string const decoded = FromHex(data); + vector buffer; + for (auto c : decoded) + buffer.push_back(static_cast(c)); + + vector points; + MemReader memReader(buffer.data(), buffer.size()); + ReaderSource src(memReader); + coding::TrafficGPSEncoder::DeserializeDataPoints(1 /* version */, src, points); + return points; +} + +class PointToMwmId final +{ +public: + PointToMwmId(shared_ptr> mwmTree, + routing::NumMwmIds const & numMwmIds, string const & dataDir) + : m_mwmTree(mwmTree) + { + numMwmIds.ForEachId([&](routing::NumMwmId numMwmId) { + string const & mwmName = numMwmIds.GetFile(numMwmId).GetName(); + string const polyFile = my::JoinPath(dataDir, BORDERS_DIR, mwmName + BORDERS_EXTENSION); + osm::LoadBorders(polyFile, m_borders[numMwmId]); + }); + } + + routing::NumMwmId FindMwmId(m2::PointD const & point, routing::NumMwmId expectedId) const + { + if (expectedId != routing::kFakeNumMwmId && m2::RegionsContain(GetBorders(expectedId), point)) + return expectedId; + + routing::NumMwmId result = routing::kFakeNumMwmId; + m2::RectD const rect = MercatorBounds::RectByCenterXYAndSizeInMeters(point, 1); + m_mwmTree->ForEachInRect(rect, [&](routing::NumMwmId numMwmId) { + if (m2::RegionsContain(GetBorders(numMwmId), point)) + { + result = numMwmId; + return; + } + }); + + return result; + } + +private: + vector const & GetBorders(routing::NumMwmId numMwmId) const + { + auto it = m_borders.find(numMwmId); + CHECK(it != m_borders.cend(), ()); + return it->second; + } + + shared_ptr> m_mwmTree; + unordered_map> m_borders; +}; +} // namespace + +namespace tracking +{ +LogParser::LogParser(shared_ptr numMwmIds, + unique_ptr> mwmTree, string const & dataDir) + : m_numMwmIds(move(numMwmIds)), m_mwmTree(move(mwmTree)), m_dataDir(dataDir) +{ + CHECK(m_numMwmIds, ()); + CHECK(m_mwmTree, ()); +} + +void LogParser::Parse(string const & logFile, MwmToTracks & mwmToTracks) const +{ + UserToTrack userToTrack; + ParseUserTracks(logFile, userToTrack); + SplitIntoMwms(userToTrack, mwmToTracks); +} + +void LogParser::ParseUserTracks(string const & logFile, UserToTrack & userToTrack) const +{ + my::Timer timer; + + std::ifstream stream(logFile); + CHECK(stream.is_open(), ("Can't open file", logFile)); + + std::regex const base_regex( + ".*(DataV0|CurrentData)\\s+aloha_id\\s*:\\s*(\\S+)\\s+.*\\|(\\w+)\\|"); + std::unordered_set usersWithOldVersion; + size_t linesCount = 0; + size_t pointsCount = 0; + + for (string line; getline(stream, line);) + { + std::smatch base_match; + if (!std::regex_match(line, base_match, base_regex)) + continue; + + CHECK_EQUAL(base_match.size(), 4, ()); + + string const version = base_match[1].str(); + string const userId = base_match[2].str(); + string const data = base_match[3].str(); + if (version != "CurrentData") + { + CHECK_EQUAL(version, "DataV0", ()); + usersWithOldVersion.insert(userId); + continue; + } + + auto const packet = ReadDataPoints(data); + if (!packet.empty()) + { + Track & track = userToTrack[userId]; + track.insert(track.end(), packet.begin(), packet.end()); + } + + ++linesCount; + pointsCount += packet.size(); + }; + + LOG(LINFO, ("Tracks parsing finished, elapsed:", timer.ElapsedSeconds(), "seconds, lines:", + linesCount, ", points", pointsCount)); + LOG(LINFO, ("Users with current version:", userToTrack.size(), ", old version:", + usersWithOldVersion.size())); +} + +void LogParser::SplitIntoMwms(UserToTrack const & userToTrack, MwmToTracks & mwmToTracks) const +{ + my::Timer timer; + + PointToMwmId const pointToMwmId(m_mwmTree, *m_numMwmIds, m_dataDir); + + for (auto & it : userToTrack) + { + string const & user = it.first; + Track const & track = it.second; + + routing::NumMwmId mwmId = routing::kFakeNumMwmId; + for (DataPoint const & point : track) + { + mwmId = pointToMwmId.FindMwmId(MercatorBounds::FromLatLon(point.m_latLon), mwmId); + if (mwmId != routing::kFakeNumMwmId) + mwmToTracks[mwmId][user].push_back(point); + else + LOG(LERROR, ("Can't match mwm region for", point.m_latLon, ", user:", user)); + } + } + + LOG(LINFO, ("Data was splitted into", mwmToTracks.size(), "mwms, elapsed:", + timer.ElapsedSeconds(), "seconds")); +} +} // namespace tracking diff --git a/track_analyzing/log_parser.hpp b/track_analyzing/log_parser.hpp new file mode 100644 index 0000000000..63df4fda87 --- /dev/null +++ b/track_analyzing/log_parser.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "track_analyzing/track.hpp" + +#include "routing/num_mwm_id.hpp" + +#include "geometry/tree4d.hpp" + +#include +#include +#include +#include + +namespace tracking +{ +class LogParser final +{ +public: + LogParser(std::shared_ptr numMwmIds, + std::unique_ptr> mwmTree, std::string const & dataDir); + + void Parse(std::string const & logFile, MwmToTracks & mwmToTracks) const; + +private: + void ParseUserTracks(std::string const & logFile, UserToTrack & userToTrack) const; + void SplitIntoMwms(UserToTrack const & userToTrack, MwmToTracks & mwmToTracks) const; + + std::shared_ptr m_numMwmIds; + std::shared_ptr> m_mwmTree; + std::string const m_dataDir; +}; +} // namespace tracking diff --git a/track_analyzing/serialization.hpp b/track_analyzing/serialization.hpp new file mode 100644 index 0000000000..f0d0b87865 --- /dev/null +++ b/track_analyzing/serialization.hpp @@ -0,0 +1,173 @@ +#pragma once + +#include "track_analyzing/track.hpp" + +#include "routing/num_mwm_id.hpp" +#include "routing/segment.hpp" + +#include "platform/country_file.hpp" + +#include "coding/read_write_utils.hpp" +#include "coding/reader.hpp" +#include "coding/traffic.hpp" +#include "coding/varint.hpp" +#include "coding/write_to_sink.hpp" +#include "coding/writer.hpp" + +#include "base/assert.hpp" +#include "base/checked_cast.hpp" + +#include +#include +#include + +namespace tracking +{ +class MwmToMatchedTracksSerializer final +{ +public: + MwmToMatchedTracksSerializer(std::shared_ptr numMwmIds) + : m_numMwmIds(numMwmIds) + { + } + + template + void Serialize(MwmToMatchedTracks const & mwmToMatchedTracks, Sink & sink) + { + WriteSize(sink, mwmToMatchedTracks.size()); + + for (auto const & mwmIt : mwmToMatchedTracks) + { + rw::Write(sink, m_numMwmIds->GetFile(mwmIt.first).GetName()); + + UserToMatchedTracks const & userToMatchedTracks = mwmIt.second; + CHECK(!userToMatchedTracks.empty(), ()); + WriteSize(sink, userToMatchedTracks.size()); + + for (auto const & userIt : userToMatchedTracks) + { + rw::Write(sink, userIt.first); + + std::vector const & tracks = userIt.second; + CHECK(!tracks.empty(), ()); + WriteSize(sink, tracks.size()); + + for (MatchedTrack const & track : tracks) + { + CHECK(!track.empty(), ()); + WriteSize(sink, track.size()); + + for (MatchedTrackPoint const & point : track) + Serialize(point.GetSegment(), sink); + + std::vector dataPoints; + for (MatchedTrackPoint const & point : track) + dataPoints.push_back(point.GetDataPoint()); + + std::vector buffer; + MemWriter memWriter(buffer); + coding::TrafficGPSEncoder::SerializeDataPoints(coding::TrafficGPSEncoder::kLatestVersion, + memWriter, dataPoints); + + WriteSize(sink, buffer.size()); + sink.Write(buffer.data(), buffer.size()); + } + } + } + } + + template + void Deserialize(MwmToMatchedTracks & mwmToMatchedTracks, Source & src) + { + mwmToMatchedTracks.clear(); + + auto const numMmws = ReadSize(src); + for (size_t iMwm = 0; iMwm < numMmws; ++iMwm) + { + std::string mwmName; + rw::Read(src, mwmName); + + auto const mwmId = m_numMwmIds->GetId(platform::CountryFile(mwmName)); + UserToMatchedTracks & userToMatchedTracks = mwmToMatchedTracks[mwmId]; + + auto const numUsers = ReadSize(src); + CHECK_NOT_EQUAL(numUsers, 0, ()); + for (size_t iUser = 0; iUser < numUsers; ++iUser) + { + std::string user; + rw::Read(src, user); + + std::vector & tracks = userToMatchedTracks[user]; + auto const numTracks = ReadSize(src); + CHECK_NOT_EQUAL(numTracks, 0, ()); + tracks.resize(numTracks); + + for (size_t iTrack = 0; iTrack < numTracks; ++iTrack) + { + auto const numSegments = ReadSize(src); + CHECK_NOT_EQUAL(numSegments, 0, ()); + std::vector segments; + segments.resize(numSegments); + + for (size_t iSeg = 0; iSeg < numSegments; ++iSeg) + Deserialize(mwmId, segments[iSeg], src); + + std::vector buffer; + auto const bufferSize = ReadSize(src); + buffer.resize(bufferSize); + src.Read(buffer.data(), bufferSize); + + MemReader memReader(buffer.data(), bufferSize); + ReaderSource memSrc(memReader); + + std::vector dataPoints; + coding::TrafficGPSEncoder::DeserializeDataPoints( + coding::TrafficGPSEncoder::kLatestVersion, memSrc, dataPoints); + CHECK_EQUAL(numSegments, dataPoints.size(), ("mwm:", mwmName, "user:", user)); + + MatchedTrack & track = tracks[iTrack]; + + for (size_t iPoint = 0; iPoint < numSegments; ++iPoint) + track.emplace_back(dataPoints[iPoint], segments[iPoint]); + } + } + } + } + +private: + static constexpr uint8_t kForward = 0; + static constexpr uint8_t kBackward = 1; + + template + static void WriteSize(Sink & sink, size_t size) + { + WriteVarUint(sink, base::checked_cast(size)); + } + + template + static size_t ReadSize(Source & src) + { + return base::checked_cast(ReadVarUint(src)); + } + + template + static void Serialize(routing::Segment const & segment, Sink & sink) + { + WriteToSink(sink, segment.GetFeatureId()); + WriteToSink(sink, segment.GetSegmentIdx()); + auto const direction = segment.IsForward() ? kForward : kBackward; + WriteToSink(sink, direction); + } + + template + void Deserialize(routing::NumMwmId numMwmId, routing::Segment & segment, Source & src) + { + auto const featureId = ReadPrimitiveFromSource(src); + auto const segmentIdx = ReadPrimitiveFromSource(src); + auto const direction = ReadPrimitiveFromSource(src); + segment = {numMwmId, featureId, segmentIdx, direction == kForward}; + } + + std::shared_ptr m_numMwmIds; +}; +} // namespace tracking diff --git a/track_analyzing/track.hpp b/track_analyzing/track.hpp new file mode 100644 index 0000000000..cf9acd9b89 --- /dev/null +++ b/track_analyzing/track.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "routing/num_mwm_id.hpp" +#include "routing/segment.hpp" + +#include "coding/traffic.hpp" + +#include +#include +#include + +namespace tracking +{ +using DataPoint = coding::TrafficGPSEncoder::DataPoint; +using Track = std::vector; +using UserToTrack = std::unordered_map; +using MwmToTracks = std::unordered_map; + +class MatchedTrackPoint final +{ +public: + MatchedTrackPoint(DataPoint const & dataPoint, routing::Segment const & segment) + : m_dataPoint(dataPoint), m_segment(segment) + { + } + + DataPoint const & GetDataPoint() const { return m_dataPoint; } + routing::Segment const & GetSegment() const { return m_segment; } + +private: + DataPoint m_dataPoint; + routing::Segment m_segment; +}; + +using MatchedTrack = std::vector; +using UserToMatchedTracks = std::unordered_map>; +using MwmToMatchedTracks = std::unordered_map; + +class TrackFilter final +{ +public: + TrackFilter(uint64_t minDuration, double minLength, double minSpeed, double maxSpeed, + bool ignoreTraffic) + : m_minDuration(minDuration) + , m_minLength(minLength) + , m_minSpeed(minSpeed) + , m_maxSpeed(maxSpeed) + , m_ignoreTraffic(ignoreTraffic) + { + } + + bool Passes(uint64_t duration, double length, double speed, bool hasTrafficPoints) const + { + if (duration < m_minDuration) + return false; + + if (length < m_minLength) + return false; + + if (speed < m_minSpeed || speed > m_maxSpeed) + return false; + + return !(m_ignoreTraffic && hasTrafficPoints); + } + +private: + uint64_t const m_minDuration; + double const m_minLength; + double const m_minSpeed; + double const m_maxSpeed; + bool m_ignoreTraffic; +}; +} // namespace tracking diff --git a/track_analyzing/track_analyzer/CMakeLists.txt b/track_analyzing/track_analyzer/CMakeLists.txt new file mode 100644 index 0000000000..83c6e0235d --- /dev/null +++ b/track_analyzing/track_analyzer/CMakeLists.txt @@ -0,0 +1,48 @@ +project(track_analyzer) + +include_directories(${OMIM_ROOT}/3party/gflags/src) + +set( + SRC + cmd_cpp_track.cpp + cmd_match.cpp + cmd_table.cpp + cmd_track.cpp + cmd_tracks.cpp + track_analyzer.cpp +) + +omim_add_executable(${PROJECT_NAME} ${SRC}) + +omim_link_libraries( + ${PROJECT_NAME} + track_analyzing + generator + map + routing + traffic + routing_common + storage + indexer + editor + platform + geometry + coding + base + opening_hours + icu + jansson + protobuf + osrm + stats_client + succinct + pugixml + gflags + oauthcpp + ${LIBZ} +) + +find_package(Boost COMPONENTS filesystem REQUIRED) +target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES}) + +link_qt5_core(${PROJECT_NAME}) diff --git a/track_analyzing/track_analyzer/cmd_cpp_track.cpp b/track_analyzing/track_analyzer/cmd_cpp_track.cpp new file mode 100644 index 0000000000..a072e2cebf --- /dev/null +++ b/track_analyzing/track_analyzer/cmd_cpp_track.cpp @@ -0,0 +1,31 @@ +#include "track_analyzing/track.hpp" +#include "track_analyzing/utils.hpp" + +#include "map/routing_helpers.hpp" + +#include "storage/storage.hpp" + +using namespace routing; +using namespace std; + +namespace tracking +{ +void CmdCppTrack(string const & trackFile, string const & mwmName, string const & user, + size_t trackIdx) +{ + storage::Storage storage; + auto const numMwmIds = CreateNumMwmIds(storage); + MwmToMatchedTracks mwmToMatchedTracks; + ReadTracks(numMwmIds, trackFile, mwmToMatchedTracks); + + MatchedTrack const & track = + GetMatchedTrack(mwmToMatchedTracks, *numMwmIds, mwmName, user, trackIdx); + + cout.precision(numeric_limits::max_digits10); + for (MatchedTrackPoint const & point : track) + { + cout << " {" << point.GetDataPoint().m_latLon.lat << ", " << point.GetDataPoint().m_latLon.lon + << "}," << endl; + } +} +} // namespace tracking diff --git a/track_analyzing/track_analyzer/cmd_match.cpp b/track_analyzing/track_analyzer/cmd_match.cpp new file mode 100644 index 0000000000..f196f6c422 --- /dev/null +++ b/track_analyzing/track_analyzer/cmd_match.cpp @@ -0,0 +1,112 @@ +#include "track_analyzing/log_parser.hpp" +#include "track_analyzing/serialization.hpp" +#include "track_analyzing/track.hpp" +#include "track_analyzing/track_matcher.hpp" +#include "track_analyzing/utils.hpp" + +#include "map/routing_helpers.hpp" + +#include "routing/num_mwm_id.hpp" + +#include "storage/country_info_getter.hpp" +#include "storage/storage.hpp" + +#include "platform/platform.hpp" + +#include "geometry/tree4d.hpp" + +#include "base/logging.hpp" +#include "base/timer.hpp" + +#include +#include + +using namespace routing; +using namespace std; +using namespace tracking; + +namespace +{ +void MatchTracks(MwmToTracks const & mwmToTracks, storage::Storage const & storage, + NumMwmIds const & numMwmIds, MwmToMatchedTracks & mwmToMatchedTracks) +{ + my::Timer timer; + + size_t tracksCount = 0; + size_t shortTracksCount = 0; + size_t pointsCount = 0; + size_t shortTrackPointsCount = 0; + size_t nonMatchedPointsCount = 0; + + ForTracksSortedByMwmName( + [&](string const & mwmName, UserToTrack const & userToTrack) { + auto const countryFile = platform::CountryFile(mwmName); + auto const mwmId = numMwmIds.GetId(countryFile); + TrackMatcher matcher(storage, mwmId, countryFile); + + auto & userToMatchedTracks = mwmToMatchedTracks[mwmId]; + + for (auto const & it : userToTrack) + { + auto & matchedTracks = userToMatchedTracks[it.first]; + matcher.MatchTrack(it.second, matchedTracks); + + if (matchedTracks.empty()) + userToMatchedTracks.erase(it.first); + } + + if (userToMatchedTracks.empty()) + mwmToMatchedTracks.erase(mwmId); + + tracksCount += matcher.GetTracksCount(); + shortTracksCount += matcher.GetShortTracksCount(); + pointsCount += matcher.GetPointsCount(); + shortTrackPointsCount += matcher.GetShortTrackPointsCount(); + nonMatchedPointsCount += matcher.GetNonMatchedPointsCount(); + + LOG(LINFO, (numMwmIds.GetFile(mwmId).GetName(), ", users:", userToTrack.size(), ", tracks:", + matcher.GetTracksCount(), ", short tracks:", matcher.GetShortTracksCount(), + ", points:", matcher.GetPointsCount(), ", short track points", + matcher.GetShortTrackPointsCount(), ", non matched points:", + matcher.GetNonMatchedPointsCount())); + }, + mwmToTracks, numMwmIds); + + LOG(LINFO, + ("Matching finished, elapsed:", timer.ElapsedSeconds(), "seconds, tracks:", tracksCount, + ", short tracks:", shortTracksCount, ", points:", pointsCount, ", short track points", + shortTrackPointsCount, ", non matched points:", nonMatchedPointsCount)); +} + +} // namespace + +namespace tracking +{ +void CmdMatch(string const & logFile, string const & trackFile) +{ + LOG(LINFO, ("Matching", logFile)); + + storage::Storage storage; + storage.RegisterAllLocalMaps(); + shared_ptr numMwmIds = CreateNumMwmIds(storage); + + Platform const & platform = GetPlatform(); + string const dataDir = platform.WritableDir(); + + unique_ptr countryInfoGetter = + storage::CountryInfoReader::CreateCountryInfoReader(platform); + unique_ptr> mwmTree = MakeNumMwmTree(*numMwmIds, *countryInfoGetter); + + tracking::LogParser parser(numMwmIds, move(mwmTree), dataDir); + MwmToTracks mwmToTracks; + parser.Parse(logFile, mwmToTracks); + + MwmToMatchedTracks mwmToMatchedTracks; + MatchTracks(mwmToTracks, storage, *numMwmIds, mwmToMatchedTracks); + + FileWriter writer(trackFile, FileWriter::OP_WRITE_TRUNCATE); + MwmToMatchedTracksSerializer serializer(numMwmIds); + serializer.Serialize(mwmToMatchedTracks, writer); + LOG(LINFO, ("Matched track was saved to", trackFile)); +} +} // namespace tracking diff --git a/track_analyzing/track_analyzer/cmd_table.cpp b/track_analyzing/track_analyzer/cmd_table.cpp new file mode 100644 index 0000000000..fa8acddb1f --- /dev/null +++ b/track_analyzing/track_analyzer/cmd_table.cpp @@ -0,0 +1,290 @@ +#include "track_analyzing/track.hpp" +#include "track_analyzing/utils.hpp" + +#include "map/routing_helpers.hpp" + +#include "routing/geometry.hpp" + +#include "routing_common/car_model.hpp" +#include "routing_common/vehicle_model.hpp" + +#include "traffic/speed_groups.hpp" + +#include "indexer/classificator.hpp" + +#include "storage/storage.hpp" + +#include "coding/file_name_utils.hpp" + +#include + +using namespace routing; +using namespace std; +using namespace tracking; + +namespace +{ +string RoadTypeToString(uint32_t type) +{ + if (type == 0) + return "unknown-type"; + + return classif().GetReadableObjectName(type); +} + +bool FeatureHasType(FeatureType const & feature, uint32_t type) +{ + bool result = false; + + feature.ForEachType([&](uint32_t featureType) { + if (featureType == type) + { + result = true; + return; + } + }); + + return result; +} + +class CarModelTypes final +{ +public: + CarModelTypes() + { + for (auto const & additionalTag : CarModel::GetAdditionalTags()) + m_tags.push_back(classif().GetTypeByPath(additionalTag.m_hwtag)); + + for (auto const & speedForType : CarModel::GetLimits()) + { + vector path; + for (char const * type : speedForType.m_types) + path.push_back(string(type)); + + m_tags.push_back(classif().GetTypeByPath(path)); + } + } + + uint32_t GetType(FeatureType const & feature) const + { + for (uint32_t type : m_tags) + { + if (FeatureHasType(feature, type)) + return type; + } + + return 0; + } + +private: + vector m_tags; +}; + +class MoveType final +{ +public: + MoveType() = default; + + MoveType(uint32_t roadType, traffic::SpeedGroup speedGroup) + : m_roadType(roadType), m_speedGroup(speedGroup) + { + } + + bool operator==(MoveType const & rhs) const + { + return m_roadType == rhs.m_roadType && m_speedGroup == rhs.m_speedGroup; + } + + bool operator<(MoveType const & rhs) const + { + if (m_roadType != rhs.m_roadType) + return m_roadType < rhs.m_roadType; + + return m_speedGroup < rhs.m_speedGroup; + } + + string ToString() const + { + ostringstream out; + out << RoadTypeToString(m_roadType) << "*" << traffic::DebugPrint(m_speedGroup); + return out.str(); + } + +private: + uint32_t m_roadType = 0; + traffic::SpeedGroup m_speedGroup = traffic::SpeedGroup::Unknown; +}; + +class MoveInfo final +{ +public: + void Add(double distance, uint64_t time) + { + m_totalDistance += distance; + m_totalTime += time; + } + + void Add(MoveInfo const & rhs) { Add(rhs.m_totalDistance, rhs.m_totalTime); } + + double GetDistance() const { return m_totalDistance; } + uint64_t GetTime() const { return m_totalTime; } + +private: + double m_totalDistance = 0.0; + uint64_t m_totalTime = 0; +}; + +class MoveTypeAggregator final +{ +public: + void Add(MoveType const moveType, MatchedTrack const & track, size_t begin, size_t end, + Geometry & geometry) + { + CHECK_LESS_OR_EQUAL(end, track.size(), ()); + + if (begin + 1 >= end) + return; + + uint64_t const time = + track[end - 1].GetDataPoint().m_timestamp - track[begin].GetDataPoint().m_timestamp; + double const length = CalcSubtrackLength(track, begin, end, geometry); + m_moveInfos[moveType].Add(length, time); + } + + void Add(MoveTypeAggregator const & rhs) + { + for (auto it : rhs.m_moveInfos) + m_moveInfos[it.first].Add(it.second); + } + + string GetSummary() const + { + ostringstream out; + out << std::fixed << std::setprecision(1); + + bool firstIteration = true; + for (auto it : m_moveInfos) + { + MoveInfo const & speedInfo = it.second; + if (firstIteration) + { + firstIteration = false; + } + else + { + out << " "; + } + + out << it.first.ToString() << ": " << speedInfo.GetDistance() << " / " << speedInfo.GetTime(); + } + + return out.str(); + } + +private: + map m_moveInfos; +}; + +class MatchedTrackPointToMoveType final +{ +public: + MatchedTrackPointToMoveType(string const & mwmFile) + : m_featuresVector(FilesContainerR(make_unique(mwmFile))) + { + } + + MoveType GetMoveType(MatchedTrackPoint const & point) + { + return MoveType(GetRoadType(point.GetSegment().GetFeatureId()), + static_cast(point.GetDataPoint().m_traffic)); + } + +private: + uint32_t GetRoadType(uint32_t featureId) + { + if (featureId == m_prevFeatureId) + return m_prevRoadType; + + FeatureType feature; + m_featuresVector.GetVector().GetByIndex(featureId, feature); + feature.ParseTypes(); + + m_prevFeatureId = featureId; + m_prevRoadType = m_carModelTypes.GetType(feature); + return m_prevRoadType; + } + + CarModelTypes const m_carModelTypes; + FeaturesVectorTest m_featuresVector; + uint32_t m_prevFeatureId = numeric_limits::max(); + uint32_t m_prevRoadType = numeric_limits::max(); +}; +} // namespace + +namespace tracking +{ +void CmdTagsTable(string const & filepath, string const & trackExtension, string const & mwmFilter, + string const & userFilter) +{ + cout << "mwm user track_idx start length time speed ... type: meters / seconds" << endl; + + storage::Storage storage; + auto numMwmIds = CreateNumMwmIds(storage); + + auto processMwm = [&](string const & mwmName, UserToMatchedTracks const & userToMatchedTracks) { + if (IsFiltered(mwmFilter, mwmName)) + return; + + shared_ptr vehicleModel = CarModelFactory().GetVehicleModelForCountry(mwmName); + string const mwmFile = + my::JoinPath(GetPlatform().WritableDir(), to_string(storage.GetCurrentDataVersion()), + mwmName + DATA_FILE_EXTENSION); + MatchedTrackPointToMoveType pointToMoveType(mwmFile); + Geometry geometry(GeometryLoader::CreateFromFile(mwmFile, vehicleModel)); + + for (auto uIt : userToMatchedTracks) + { + string const & user = uIt.first; + if (IsFiltered(userFilter, user)) + continue; + + for (size_t trackIdx = 0; trackIdx < uIt.second.size(); ++trackIdx) + { + MatchedTrack const & track = uIt.second[trackIdx]; + uint64_t const start = track.front().GetDataPoint().m_timestamp; + uint64_t const timeElapsed = track.back().GetDataPoint().m_timestamp - start; + double const length = CalcTrackLength(track, geometry); + double const speed = CalcSpeedKMpH(length, timeElapsed); + + MoveTypeAggregator aggregator; + + MoveType currentType(numeric_limits::max(), traffic::SpeedGroup::Count); + size_t subTrackBegin = 0; + + for (size_t i = 0; i < track.size(); ++i) + { + MoveType const type = pointToMoveType.GetMoveType(track[i]); + if (type == currentType) + continue; + + aggregator.Add(currentType, track, subTrackBegin, i, geometry); + currentType = type; + subTrackBegin = i; + } + aggregator.Add(currentType, track, subTrackBegin, track.size(), geometry); + + cout << mwmName << " " << user << " " << trackIdx << " " + << my::SecondsSinceEpochToString(start) << " " << length << " " << timeElapsed << " " + << speed << " " << aggregator.GetSummary() << endl; + } + } + }; + + auto processTrack = [&](string const & filename, MwmToMatchedTracks const & mwmToMatchedTracks) { + LOG(LINFO, ("Processing", filename)); + ForTracksSortedByMwmName(processMwm, mwmToMatchedTracks, *numMwmIds); + }; + + ForEachTrackFile(processTrack, filepath, trackExtension, numMwmIds); +} +} // namespace tracking diff --git a/track_analyzing/track_analyzer/cmd_track.cpp b/track_analyzing/track_analyzer/cmd_track.cpp new file mode 100644 index 0000000000..48df1303ae --- /dev/null +++ b/track_analyzing/track_analyzer/cmd_track.cpp @@ -0,0 +1,77 @@ +#include "track_analyzing/track.hpp" +#include "track_analyzing/utils.hpp" + +#include "map/routing_helpers.hpp" + +#include "routing/geometry.hpp" + +#include "routing_common/car_model.hpp" +#include "routing_common/vehicle_model.hpp" + +#include "indexer/feature.hpp" +#include "indexer/features_vector.hpp" + +#include "storage/storage.hpp" + +#include "coding/file_name_utils.hpp" + +#include "geometry/mercator.hpp" + +using namespace routing; +using namespace std; + +namespace tracking +{ +void CmdTrack(string const & trackFile, string const & mwmName, string const & user, + size_t trackIdx) +{ + storage::Storage storage; + auto const numMwmIds = CreateNumMwmIds(storage); + MwmToMatchedTracks mwmToMatchedTracks; + ReadTracks(numMwmIds, trackFile, mwmToMatchedTracks); + + MatchedTrack const & track = + GetMatchedTrack(mwmToMatchedTracks, *numMwmIds, mwmName, user, trackIdx); + + string const mwmFile = + my::JoinPath(GetPlatform().WritableDir(), to_string(storage.GetCurrentDataVersion()), + mwmName + DATA_FILE_EXTENSION); + shared_ptr vehicleModel = CarModelFactory().GetVehicleModelForCountry(mwmName); + FeaturesVectorTest featuresVector(FilesContainerR(make_unique(mwmFile))); + Geometry geometry(GeometryLoader::CreateFromFile(mwmFile, vehicleModel)); + + uint64_t const duration = + track.back().GetDataPoint().m_timestamp - track.front().GetDataPoint().m_timestamp; + double const length = CalcTrackLength(track, geometry); + double const averageSpeed = CalcSpeedKMpH(length, duration); + LOG(LINFO, ("Mwm:", mwmName, ", user:", user, ", points:", track.size(), "duration:", duration, + "length:", length, ", speed:", averageSpeed, "km/h")); + + for (size_t i = 0; i < track.size(); ++i) + { + MatchedTrackPoint const & point = track[i]; + FeatureType feature; + featuresVector.GetVector().GetByIndex(point.GetSegment().GetFeatureId(), feature); + + double speed = 0.0; + uint64_t elapsed = 0; + double distance = 0.0; + if (i > 0) + { + MatchedTrackPoint const & prevPoint = track[i - 1]; + elapsed = point.GetDataPoint().m_timestamp - prevPoint.GetDataPoint().m_timestamp; + distance = MercatorBounds::DistanceOnEarth( + MercatorBounds::FromLatLon(prevPoint.GetDataPoint().m_latLon), + MercatorBounds::FromLatLon(point.GetDataPoint().m_latLon)); + } + + if (elapsed != 0) + speed = CalcSpeedKMpH(distance, elapsed); + + LOG(LINFO, (my::SecondsSinceEpochToString(point.GetDataPoint().m_timestamp), + point.GetDataPoint().m_latLon, point.GetSegment(), ", traffic:", + point.GetDataPoint().m_traffic, ", distance:", distance, ", elapsed:", elapsed, + ", speed:", speed)); + } +} +} // namespace tracking diff --git a/track_analyzing/track_analyzer/cmd_tracks.cpp b/track_analyzing/track_analyzer/cmd_tracks.cpp new file mode 100644 index 0000000000..57e3da6f32 --- /dev/null +++ b/track_analyzing/track_analyzer/cmd_tracks.cpp @@ -0,0 +1,233 @@ +#include "track_analyzing/track.hpp" +#include "track_analyzing/utils.hpp" + +#include "map/routing_helpers.hpp" + +#include "routing_common/car_model.hpp" +#include "routing_common/vehicle_model.hpp" + +#include "routing/edge_estimator.hpp" +#include "routing/geometry.hpp" + +#include "storage/storage.hpp" + +#include "platform/platform.hpp" + +#include "coding/file_name_utils.hpp" + +#include "base/logging.hpp" +#include "base/timer.hpp" + +#include +#include + +using namespace routing; +using namespace std; +using namespace tracking; + +namespace +{ +class TrackStats final +{ +public: + void AddUsers(size_t numUsers) { m_numUsers += numUsers; } + void AddTracks(size_t numTracks) { m_numTracks += numTracks; } + void AddPoints(size_t numPoints) { m_numPoints += numPoints; } + + void Add(TrackStats const & rhs) + { + m_numUsers += rhs.m_numUsers; + m_numTracks += rhs.m_numTracks; + m_numPoints += rhs.m_numPoints; + } + + string GetSummary() const + { + ostringstream out; + out << "users: " << m_numUsers << ", tracks: " << m_numTracks << ", points: " << m_numPoints; + return out.str(); + } + + bool IsEmpty() const { return m_numPoints == 0; } + +private: + size_t m_numUsers = 0; + size_t m_numTracks = 0; + size_t m_numPoints = 0; +}; + +class ErrorStat final +{ +public: + void Add(double value) + { + m_count += 1.0; + m_squares += value * value; + m_min = min(m_min, value); + m_max = max(m_max, value); + } + + double GetDeviation() const + { + CHECK_GREATER(m_count, 0.0, ()); + return std::sqrt(m_squares / m_count); + } + + double GetMin() const { return m_min; } + double GetMax() const { return m_max; } + +private: + double m_count = 0.0; + double m_squares = 0.0; + double m_min = numeric_limits::max(); + double m_max = numeric_limits::min(); +}; + +bool TrackHasTrafficPoints(MatchedTrack const & track) +{ + for (MatchedTrackPoint const & point : track) + { + size_t const index = static_cast(point.GetDataPoint().m_traffic); + CHECK_LESS(index, static_cast(traffic::SpeedGroup::Count), ()); + if (traffic::kSpeedGroupThresholdPercentage[index] != 100) + return true; + } + + return false; +} + +double EstimateDuration(MatchedTrack const & track, shared_ptr estimator, + Geometry & geometry) +{ + double result = 0.0; + Segment segment; + + for (MatchedTrackPoint const & point : track) + { + if (point.GetSegment() == segment) + continue; + + segment = point.GetSegment(); + result += estimator->CalcSegmentWeight(segment, geometry.GetRoad(segment.GetFeatureId())); + } + + return result; +} +} // namespace + +namespace tracking +{ +void CmdTracks(string const & filepath, string const & trackExtension, string const & mwmFilter, + string const & userFilter, TrackFilter const & filter, bool noTrackLogs, + bool noMwmLogs, bool noWorldLogs) +{ + storage::Storage storage; + auto numMwmIds = CreateNumMwmIds(storage); + + if (!mwmFilter.empty() && !numMwmIds->ContainsFile(platform::CountryFile(mwmFilter))) + MYTHROW(MessageException, ("Mwm", mwmFilter, "does not exist")); + + map mwmToStats; + ErrorStat absoluteError; + ErrorStat relativeError; + + auto processMwm = [&](string const & mwmName, UserToMatchedTracks const & userToMatchedTracks) { + if (IsFiltered(mwmFilter, mwmName)) + return; + + TrackStats & mwmStats = mwmToStats[mwmName]; + + shared_ptr vehicleModel = CarModelFactory().GetVehicleModelForCountry(mwmName); + + Geometry geometry(GeometryLoader::CreateFromFile( + my::JoinPath(GetPlatform().WritableDir(), to_string(storage.GetCurrentDataVersion()), + mwmName + DATA_FILE_EXTENSION), + vehicleModel)); + + shared_ptr estimator = + EdgeEstimator::CreateForCar(nullptr, vehicleModel->GetMaxSpeed()); + + for (auto it : userToMatchedTracks) + { + string const & user = it.first; + if (IsFiltered(userFilter, user)) + continue; + + vector const & tracks = it.second; + if (!noTrackLogs) + cout << mwmName << ", user: " << user << endl; + + bool thereAreUnfilteredTracks = false; + for (MatchedTrack const & track : tracks) + { + DataPoint const & start = track.front().GetDataPoint(); + DataPoint const & finish = track.back().GetDataPoint(); + + double const length = CalcTrackLength(track, geometry); + uint64_t const duration = finish.m_timestamp - start.m_timestamp; + double const speed = CalcSpeedKMpH(length, duration); + bool const hasTrafficPoints = TrackHasTrafficPoints(track); + + if (!filter.Passes(duration, length, speed, hasTrafficPoints)) + continue; + + double const estimatedDuration = EstimateDuration(track, estimator, geometry); + double const timeError = estimatedDuration - static_cast(duration); + + if (!noTrackLogs) + { + cout << fixed << setprecision(1) << " points: " << track.size() << ", length: " << length + << ", duration: " << duration << ", estimated duration: " << estimatedDuration + << ", speed: " << speed << ", traffic: " << hasTrafficPoints + << ", departure: " << my::SecondsSinceEpochToString(start.m_timestamp) + << ", arrival: " << my::SecondsSinceEpochToString(finish.m_timestamp) + << setprecision(numeric_limits::max_digits10) + << ", start: " << start.m_latLon.lat << ", " << start.m_latLon.lon + << ", finish: " << finish.m_latLon.lat << ", " << finish.m_latLon.lon << endl; + } + + mwmStats.AddTracks(1); + mwmStats.AddPoints(track.size()); + absoluteError.Add(timeError); + relativeError.Add(timeError / static_cast(duration)); + thereAreUnfilteredTracks = true; + } + + if (thereAreUnfilteredTracks) + mwmStats.AddUsers(1); + } + }; + + auto processFile = [&](string const & filename, MwmToMatchedTracks const & mwmToMatchedTracks) { + LOG(LINFO, ("Processing", filename)); + ForTracksSortedByMwmName(processMwm, mwmToMatchedTracks, *numMwmIds); + }; + + ForEachTrackFile(processFile, filepath, trackExtension, numMwmIds); + + if (!noMwmLogs) + { + cout << endl; + for (auto it : mwmToStats) + { + if (!it.second.IsEmpty()) + cout << it.first << ": " << it.second.GetSummary() << endl; + } + } + + if (!noWorldLogs) + { + TrackStats worldStats; + for (auto it : mwmToStats) + worldStats.Add(it.second); + + cout << endl << "World: " << worldStats.GetSummary() << endl; + cout << fixed << setprecision(1) + << "Absolute error: deviation: " << absoluteError.GetDeviation() + << ", min: " << absoluteError.GetMin() << ", max: " << absoluteError.GetMax() << endl; + cout << fixed << setprecision(3) + << "Relative error: deviation: " << relativeError.GetDeviation() + << ", min: " << relativeError.GetMin() << ", max: " << relativeError.GetMax() << endl; + } +} +} // namespace tracking diff --git a/track_analyzing/track_analyzer/track_analyzer.cpp b/track_analyzing/track_analyzer/track_analyzer.cpp new file mode 100644 index 0000000000..c73c0d8246 --- /dev/null +++ b/track_analyzing/track_analyzer/track_analyzer.cpp @@ -0,0 +1,118 @@ +#include "track_analyzing/exceptions.hpp" +#include "track_analyzing/track.hpp" + +#include "indexer/classificator.hpp" +#include "indexer/classificator_loader.hpp" + +#include "3party/gflags/src/gflags/gflags.h" + +using namespace std; +using namespace tracking; + +namespace +{ +#define DEFINE_string_ext(name, value, description) \ + DEFINE_string(name, value, description); \ + \ + string const & Demand_##name() \ + { \ + if (FLAGS_##name.empty()) \ + MYTHROW(MessageException, (string("Specify the argument --") + #name)); \ + \ + return FLAGS_##name; \ + } + +DEFINE_string_ext(cmd, "", "command: match, info, cpptrack"); +DEFINE_string_ext(in, "", "input log file name"); +DEFINE_string(out, "", "output track file name"); +DEFINE_string_ext(mwm, "", "short mwm name"); +DEFINE_string_ext(user, "", "user id"); +DEFINE_int32(track, -1, "track index"); + +DEFINE_string(track_extension, ".track", "track files extension"); +DEFINE_bool(no_world_logs, false, "don't print world summary logs"); +DEFINE_bool(no_mwm_logs, false, "don't print logs per mwm"); +DEFINE_bool(no_track_logs, false, "don't print logs per track"); + +DEFINE_uint64(min_duration, 5 * 60, "minimal track duration in seconds"); +DEFINE_double(min_length, 1000.0, "minimal track length in meters"); +DEFINE_double(min_speed, 15.0, "minimal track average speed in km/hour"); +DEFINE_double(max_speed, 110.0, "maximum track average speed in km/hour"); +DEFINE_bool(ignore_traffic, true, "ignore tracks with traffic data"); + +size_t Demand_track() +{ + if (FLAGS_track < 0) + MYTHROW(MessageException, ("Specify the --track key")); + + return static_cast(FLAGS_track); +} +} // namespace + +namespace tracking +{ +void CmdCppTrack(string const & trackFile, string const & mwmName, string const & user, + size_t trackIdx); +void CmdMatch(string const & logFile, string const & trackFile); +void CmdTagsTable(string const & filepath, string const & trackExtension, string const & mwmFilter, + string const & userFilter); +void CmdTrack(string const & trackFile, string const & mwmName, string const & user, + size_t trackIdx); +void CmdTracks(string const & filepath, string const & trackExtension, string const & mwmFilter, + string const & userFilter, TrackFilter const & filter, bool noTrackLogs, + bool noMwmLogs, bool noWorldLogs); +} // namespace tracking + +int main(int argc, char ** argv) +{ + google::ParseCommandLineFlags(&argc, &argv, true); + string const & cmd = Demand_cmd(); + + classificator::Load(); + classif().SortClassificator(); + + try + { + if (cmd == "match") + { + string const & logFile = Demand_in(); + CmdMatch(logFile, FLAGS_out.empty() ? logFile + ".track" : FLAGS_out); + } + else if (cmd == "tracks") + { + TrackFilter const filter(FLAGS_min_duration, FLAGS_min_length, FLAGS_min_speed, + FLAGS_max_speed, FLAGS_ignore_traffic); + CmdTracks(Demand_in(), FLAGS_track_extension, FLAGS_mwm, FLAGS_user, filter, + FLAGS_no_track_logs, FLAGS_no_mwm_logs, FLAGS_no_world_logs); + } + else if (cmd == "track") + { + CmdTrack(Demand_in(), Demand_mwm(), Demand_user(), Demand_track()); + } + else if (cmd == "cpptrack") + { + CmdCppTrack(Demand_in(), Demand_mwm(), Demand_user(), Demand_track()); + } + else if (cmd == "table") + { + CmdTagsTable(Demand_in(), FLAGS_track_extension, FLAGS_mwm, FLAGS_user); + } + else + { + LOG(LWARNING, ("Unknown command", FLAGS_cmd)); + return 1; + } + } + catch (MessageException const & e) + { + LOG(LWARNING, (e.Msg())); + return 1; + } + catch (RootException const & e) + { + LOG(LERROR, (e.what())); + return 1; + } + + return 0; +} diff --git a/track_analyzing/track_matcher.cpp b/track_analyzing/track_matcher.cpp new file mode 100644 index 0000000000..068a2485e4 --- /dev/null +++ b/track_analyzing/track_matcher.cpp @@ -0,0 +1,240 @@ +#include "track_analyzing/track_matcher.hpp" + +#include + +#include + +#include + +#include + +using namespace tracking; +using namespace std; + +namespace +{ +double constexpr kMatchingRange = 20.0; +uint64_t constexpr kShortTrackDuration = 60; + +double DistanceToSegment(m2::PointD const & segmentBegin, m2::PointD const & segmentEnd, + m2::PointD const & point) +{ + m2::ProjectionToSection projection; + projection.SetBounds(segmentBegin, segmentEnd); + m2::PointD const projectionPoint = projection(point); + return MercatorBounds::DistanceOnEarth(point, projectionPoint); +} + +double DistanceToSegment(routing::Segment const & segment, m2::PointD const & point, + routing::IndexGraph & indexGraph) +{ + return DistanceToSegment(indexGraph.GetGeometry().GetPoint(segment.GetRoadPoint(false)), + indexGraph.GetGeometry().GetPoint(segment.GetRoadPoint(true)), point); +} + +bool EdgesContain(vector const & edges, routing::Segment const & segment) +{ + for (auto const & edge : edges) + { + if (edge.GetTarget() == segment) + return true; + } + + return false; +} +} // namespace + +namespace tracking +{ +// TrackMatcher ------------------------------------------------------------------------------------ +TrackMatcher::TrackMatcher(storage::Storage const & storage, routing::NumMwmId mwmId, + platform::CountryFile const & countryFile) + : m_mwmId(mwmId) + , m_vehicleModel(routing::CarModelFactory().GetVehicleModelForCountry(countryFile.GetName())) +{ + auto localCountryFile = storage.GetLatestLocalFile(countryFile); + CHECK(localCountryFile, ("Can't find latest country file for", countryFile.GetName())); + auto registerResult = m_index.Register(*localCountryFile); + CHECK_EQUAL(registerResult.second, MwmSet::RegResult::Success, + ("Can't register mwm", countryFile.GetName())); + + m_graph = make_unique( + routing::GeometryLoader::Create(m_index, registerResult.first, m_vehicleModel), + routing::EdgeEstimator::CreateForCar(nullptr /* trafficStash */, + m_vehicleModel->GetMaxSpeed())); + + MwmSet::MwmHandle const handle = m_index.GetMwmHandleByCountryFile(countryFile); + routing::DeserializeIndexGraph(*handle.GetValue(), *m_graph); +} + +void TrackMatcher::MatchTrack(vector const & track, vector & matchedTracks) +{ + m_pointsCount += track.size(); + + vector steps; + for (auto const & routePoint : track) + steps.emplace_back(routePoint); + + for (size_t trackBegin = 0; trackBegin < steps.size();) + { + for (; trackBegin < steps.size(); ++trackBegin) + { + steps[trackBegin].FillCandidatesWithNearbySegments(m_index, *m_vehicleModel, m_mwmId); + if (steps[trackBegin].HasCandidates()) + break; + + ++m_nonMatchedPointsCount; + } + + if (trackBegin >= steps.size()) + break; + + size_t trackEnd = trackBegin; + for (; trackEnd < steps.size() - 1; ++trackEnd) + { + Step & nextStep = steps[trackEnd + 1]; + nextStep.FillCandidates(steps[trackEnd], *m_graph); + if (!nextStep.HasCandidates()) + break; + } + + steps[trackEnd].ChooseNearestSegment(); + + for (size_t i = trackEnd; i > trackBegin; --i) + steps[i - 1].ChooseSegment(steps[i], *m_graph); + + ++m_tracksCount; + + uint64_t const trackTime = + steps[trackEnd].GetDataPoint().m_timestamp - steps[trackBegin].GetDataPoint().m_timestamp; + if (trackTime < kShortTrackDuration) + { + ++m_shortTracksCount; + m_shortTrackPointsCount += trackEnd + 1 - trackBegin; + } + else + { + matchedTracks.push_back({}); + MatchedTrack & matchedTrack = matchedTracks.back(); + for (size_t i = trackBegin; i <= trackEnd; ++i) + { + Step const & step = steps[i]; + matchedTrack.emplace_back(step.GetDataPoint(), step.GetSegment()); + } + } + + trackBegin = trackEnd + 1; + } +} + +// TrackMatcher::Step ------------------------------------------------------------------------------ +TrackMatcher::Step::Step(DataPoint const & dataPoint) + : m_dataPoint(dataPoint), m_point(MercatorBounds::FromLatLon(dataPoint.m_latLon)) +{ +} + +void TrackMatcher::Step::FillCandidatesWithNearbySegments( + Index const & index, routing::IVehicleModel const & vehicleModel, routing::NumMwmId mwmId) +{ + index.ForEachInRect( + [&](FeatureType const & ft) { + if (!ft.GetID().IsValid()) + return; + + if (ft.GetID().m_mwmId.GetInfo()->GetType() != MwmInfo::COUNTRY) + return; + + if (!vehicleModel.IsRoad(ft)) + return; + + ft.ParseGeometry(FeatureType::BEST_GEOMETRY); + + for (size_t segIdx = 0; segIdx + 1 < ft.GetPointsCount(); ++segIdx) + { + double const distance = + DistanceToSegment(ft.GetPoint(segIdx), ft.GetPoint(segIdx + 1), m_point); + if (distance < kMatchingRange) + { + m_candidates.emplace_back( + routing::Segment(mwmId, ft.GetID().m_index, static_cast(segIdx), true), + distance); + + if (!vehicleModel.IsOneWay(ft)) + m_candidates.emplace_back( + routing::Segment(mwmId, ft.GetID().m_index, static_cast(segIdx), false), + distance); + } + } + }, + MercatorBounds::RectByCenterXYAndSizeInMeters(m_point, kMatchingRange), + scales::GetUpperScale()); +} + +void TrackMatcher::Step::FillCandidates(Step const & previousStep, routing::IndexGraph & graph) +{ + vector edges; + + for (Candidate const & candidate : previousStep.m_candidates) + { + routing::Segment const & segment = candidate.GetSegment(); + m_candidates.emplace_back(segment, DistanceToSegment(segment, m_point, graph)); + + edges.clear(); + graph.GetEdgeList(segment, true, edges); + + for (routing::SegmentEdge const & edge : edges) + { + routing::Segment const & target = edge.GetTarget(); + if (!segment.IsInverse(target)) + m_candidates.emplace_back(target, DistanceToSegment(target, m_point, graph)); + } + } + + my::SortUnique(m_candidates); + + m_candidates.erase(remove_if(m_candidates.begin(), m_candidates.end(), + [&](Candidate const & candidate) { + return candidate.GetDistance() > kMatchingRange; + }), + m_candidates.end()); +} + +void TrackMatcher::Step::ChooseSegment(Step const & nextStep, routing::IndexGraph & indexGraph) +{ + CHECK(!m_candidates.empty(), ()); + + double minDistance = numeric_limits::max(); + + vector edges; + indexGraph.GetEdgeList(nextStep.m_segment, false, edges); + edges.emplace_back(nextStep.m_segment, 0.0 /* weight */); + + for (Candidate const & candidate : m_candidates) + { + if (candidate.GetDistance() < minDistance && EdgesContain(edges, candidate.GetSegment())) + { + minDistance = candidate.GetDistance(); + m_segment = candidate.GetSegment(); + } + } + + CHECK_LESS(minDistance, numeric_limits::max(), + ("Can't find previous segment for", nextStep.m_segment)); +} + +void TrackMatcher::Step::ChooseNearestSegment() +{ + CHECK(!m_candidates.empty(), ()); + + double minDistance = numeric_limits::max(); + + for (Candidate const & candidate : m_candidates) + { + if (candidate.GetDistance() < minDistance) + { + minDistance = candidate.GetDistance(); + m_segment = candidate.GetSegment(); + } + } +} +} // namespace tracking diff --git a/track_analyzing/track_matcher.hpp b/track_analyzing/track_matcher.hpp new file mode 100644 index 0000000000..376f8abd1b --- /dev/null +++ b/track_analyzing/track_matcher.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include "track_analyzing/track.hpp" + +#include "routing/index_graph.hpp" +#include "routing/num_mwm_id.hpp" +#include "routing/segment.hpp" + +#include "indexer/index.hpp" + +#include + +#include "geometry/point2d.hpp" + +#include +#include +#include + +namespace tracking +{ +class TrackMatcher final +{ +public: + TrackMatcher(storage::Storage const & storage, routing::NumMwmId mwmId, + platform::CountryFile const & countryFile); + + void MatchTrack(std::vector const & track, std::vector & matchedTracks); + + size_t GetTracksCount() const { return m_tracksCount; } + size_t GetShortTracksCount() const { return m_shortTracksCount; } + size_t GetPointsCount() const { return m_pointsCount; } + size_t GetShortTrackPointsCount() const { return m_shortTrackPointsCount; } + size_t GetNonMatchedPointsCount() const { return m_nonMatchedPointsCount; } + +private: + class Candidate final + { + public: + Candidate(routing::Segment segment, double distance) : m_segment(segment), m_distance(distance) + { + } + + routing::Segment const & GetSegment() const { return m_segment; } + double GetDistance() const { return m_distance; } + bool operator==(Candidate const & candidate) const { return m_segment == candidate.m_segment; } + bool operator<(Candidate const & candidate) const { return m_segment < candidate.m_segment; } + + private: + routing::Segment m_segment; + double m_distance; + }; + + class Step final + { + public: + explicit Step(DataPoint const & dataPoint); + + DataPoint const & GetDataPoint() const { return m_dataPoint; } + routing::Segment const & GetSegment() const { return m_segment; } + + bool HasCandidates() const { return !m_candidates.empty(); } + + void FillCandidatesWithNearbySegments(Index const & index, + routing::IVehicleModel const & vehicleModel, + routing::NumMwmId mwmId); + void FillCandidates(Step const & previousStep, routing::IndexGraph & graph); + void ChooseSegment(Step const & nextStep, routing::IndexGraph & indexGraph); + void ChooseNearestSegment(); + + private: + DataPoint m_dataPoint; + m2::PointD m_point; + routing::Segment m_segment; + std::vector m_candidates; + }; + + routing::NumMwmId const m_mwmId; + Index m_index; + std::shared_ptr m_vehicleModel; + std::unique_ptr m_graph; + size_t m_tracksCount = 0; + size_t m_shortTracksCount = 0; + size_t m_pointsCount = 0; + size_t m_shortTrackPointsCount = 0; + size_t m_nonMatchedPointsCount = 0; +}; +} // namespace tracking diff --git a/track_analyzing/utils.cpp b/track_analyzing/utils.cpp new file mode 100644 index 0000000000..c19d397c8e --- /dev/null +++ b/track_analyzing/utils.cpp @@ -0,0 +1,92 @@ +#include "track_analyzing/utils.hpp" + +#include "track_analyzing/serialization.hpp" + +#include "routing/segment.hpp" + +#include + +using namespace routing; +using namespace std; + +namespace tracking +{ +double CalcSubtrackLength(MatchedTrack const & track, size_t begin, size_t end, Geometry & geometry) +{ + CHECK_LESS_OR_EQUAL(end, track.size(), ()); + + double length = 0.0; + + Segment prevSegment; + for (size_t i = begin; i < end; ++i) + { + MatchedTrackPoint const & point = track[i]; + Segment const & segment = point.GetSegment(); + if (segment != prevSegment) + { + length += MercatorBounds::DistanceOnEarth(geometry.GetPoint(segment.GetRoadPoint(false)), + geometry.GetPoint(segment.GetRoadPoint(true))); + prevSegment = segment; + } + } + + return length; +} + +double CalcTrackLength(MatchedTrack const & track, Geometry & geometry) +{ + return CalcSubtrackLength(track, 0, track.size(), geometry); +} + +double CalcSpeedKMpH(double meters, uint64_t secondsElapsed) +{ + CHECK_GREATER(secondsElapsed, 0, ()); + double constexpr kMPS2KMPH = 60.0 * 60.0 / 1000.0; + return kMPS2KMPH * meters / static_cast(secondsElapsed); +} + +bool IsFiltered(string const & argument, string const & variable) +{ + return !argument.empty() && variable != argument; +} + +void ReadTracks(shared_ptr numMwmIds, string const & filename, + MwmToMatchedTracks & mwmToMatchedTracks) +{ + FileReader reader(filename); + ReaderSource src(reader); + MwmToMatchedTracksSerializer serializer(numMwmIds); + serializer.Deserialize(mwmToMatchedTracks, src); +} + +MatchedTrack const & GetMatchedTrack(MwmToMatchedTracks const & mwmToMatchedTracks, + NumMwmIds const & numMwmIds, string const & mwmName, + string const & user, size_t trackIdx) +{ + auto const countryFile = platform::CountryFile(mwmName); + if (!numMwmIds.ContainsFile(countryFile)) + MYTHROW(MessageException, ("Invalid mwm name", mwmName)); + + NumMwmId const numMwmId = numMwmIds.GetId(countryFile); + + auto mIt = mwmToMatchedTracks.find(numMwmId); + if (mIt == mwmToMatchedTracks.cend()) + MYTHROW(MessageException, ("There is no tracks for mwm", mwmName)); + + UserToMatchedTracks const & userToMatchedTracks = mIt->second; + + auto uIt = userToMatchedTracks.find(user); + if (uIt == userToMatchedTracks.end()) + MYTHROW(MessageException, ("There is no user", user)); + + vector const & tracks = uIt->second; + + if (trackIdx >= tracks.size()) + { + MYTHROW(MessageException, ("There is no track", trackIdx, "for user", user, ", she has", + tracks.size(), "tracks only")); + } + + return tracks[trackIdx]; +} +} // namespace tracking \ No newline at end of file diff --git a/track_analyzing/utils.hpp b/track_analyzing/utils.hpp new file mode 100644 index 0000000000..b10f180d81 --- /dev/null +++ b/track_analyzing/utils.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include "track_analyzing/exceptions.hpp" +#include "track_analyzing/track.hpp" + +#include "routing/geometry.hpp" +#include "routing/num_mwm_id.hpp" + +#include + +#include +#include +#include +#include + +namespace tracking +{ +double CalcSubtrackLength(MatchedTrack const & track, size_t begin, size_t end, + routing::Geometry & geometry); +double CalcTrackLength(MatchedTrack const & track, routing::Geometry & geometry); +double CalcSpeedKMpH(double meters, uint64_t secondsElapsed); +bool IsFiltered(std::string const & argument, std::string const & variable); +void ReadTracks(std::shared_ptr numMwmIds, std::string const & filename, + MwmToMatchedTracks & mwmToMatchedTracks); +MatchedTrack const & GetMatchedTrack(MwmToMatchedTracks const & mwmToMatchedTracks, + routing::NumMwmIds const & numMwmIds, + std::string const & mwmName, std::string const & user, + size_t trackIdx); + +template +void ForTracksSortedByMwmName(ToDo && toDo, MwmToTracks const & mwmToTracks, + routing::NumMwmIds const & numMwmIds) +{ + std::vector mwmNames; + mwmNames.reserve(mwmToTracks.size()); + for (auto const & it : mwmToTracks) + mwmNames.push_back(numMwmIds.GetFile(it.first).GetName()); + sort(mwmNames.begin(), mwmNames.end()); + + for (auto const & mwmName : mwmNames) + { + auto const mwmId = numMwmIds.GetId(platform::CountryFile(mwmName)); + auto mwmIt = mwmToTracks.find(mwmId); + CHECK(mwmIt != mwmToTracks.cend(), ()); + toDo(mwmName, mwmIt->second); + } +} + +template +void ForEachTrackFile(ToDo && toDo, std::string const & filepath, std::string const & extension, + shared_ptr numMwmIds) +{ + if (!boost::filesystem::exists(filepath.c_str())) + MYTHROW(MessageException, ("File doesn't exist", filepath)); + + if (boost::filesystem::is_regular(filepath)) + { + MwmToMatchedTracks mwmToMatchedTracks; + ReadTracks(numMwmIds, filepath, mwmToMatchedTracks); + toDo(filepath, mwmToMatchedTracks); + return; + } + + if (boost::filesystem::is_directory(filepath)) + { + for (boost::filesystem::recursive_directory_iterator it(filepath.c_str()), end; it != end; ++it) + { + boost::filesystem::path const & path = it->path(); + if (!boost::filesystem::is_regular(path)) + continue; + + if (path.extension() != extension) + continue; + + MwmToMatchedTracks mwmToMatchedTracks; + string const & filename = path.string(); + ReadTracks(numMwmIds, filename, mwmToMatchedTracks); + toDo(filename, mwmToMatchedTracks); + } + + return; + } + + MYTHROW(MessageException, (filepath, "is neither a regular file nor a directory't exist")); +} +} // namespace tracking diff --git a/xcode/map/map.xcodeproj/project.pbxproj b/xcode/map/map.xcodeproj/project.pbxproj index fe45240e13..56e3b617df 100644 --- a/xcode/map/map.xcodeproj/project.pbxproj +++ b/xcode/map/map.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 0C2B73DE1E92AB9900530BB8 /* local_ads_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0C2B73DC1E92AB9900530BB8 /* local_ads_manager.cpp */; }; 0C2B73DF1E92AB9900530BB8 /* local_ads_manager.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C2B73DD1E92AB9900530BB8 /* local_ads_manager.hpp */; }; + 0CD3BA421ECDF30C0029AA81 /* routing_helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0CD3BA401ECDF30C0029AA81 /* routing_helpers.cpp */; }; + 0CD3BA431ECDF30C0029AA81 /* routing_helpers.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0CD3BA411ECDF30C0029AA81 /* routing_helpers.hpp */; }; 342D833A1D5233E8000D8AEA /* displacement_mode_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 342D83381D5233E8000D8AEA /* displacement_mode_manager.cpp */; }; 342D833B1D5233E8000D8AEA /* displacement_mode_manager.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 342D83391D5233E8000D8AEA /* displacement_mode_manager.hpp */; }; 34583BCF1C88556800F94664 /* place_page_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34583BCD1C88556800F94664 /* place_page_info.cpp */; }; @@ -31,8 +33,6 @@ 45580ABE1E2CBD5E00CD535D /* benchmark_tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45580ABC1E2CBD5E00CD535D /* benchmark_tools.cpp */; }; 45580ABF1E2CBD5E00CD535D /* benchmark_tools.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 45580ABD1E2CBD5E00CD535D /* benchmark_tools.hpp */; }; 45D287671E966E3400587F05 /* liblocal_ads.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45D287661E966E3400587F05 /* liblocal_ads.a */; }; - 56B6EAF81EA4BAF00037D963 /* mwm_tree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B6EAF61EA4BAF00037D963 /* mwm_tree.cpp */; }; - 56B6EAF91EA4BAF00037D963 /* mwm_tree.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B6EAF71EA4BAF00037D963 /* mwm_tree.hpp */; }; 670E39401C46C5C700E9C0A6 /* gps_tracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670E393E1C46C5C700E9C0A6 /* gps_tracker.cpp */; }; 670E39411C46C5C700E9C0A6 /* gps_tracker.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 670E393F1C46C5C700E9C0A6 /* gps_tracker.hpp */; }; 674231CB1DF984F600913FEB /* libtraffic.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674231CA1DF984F600913FEB /* libtraffic.a */; }; @@ -137,6 +137,8 @@ /* Begin PBXFileReference section */ 0C2B73DC1E92AB9900530BB8 /* local_ads_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = local_ads_manager.cpp; sourceTree = ""; }; 0C2B73DD1E92AB9900530BB8 /* local_ads_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = local_ads_manager.hpp; sourceTree = ""; }; + 0CD3BA401ECDF30C0029AA81 /* routing_helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = routing_helpers.cpp; sourceTree = ""; }; + 0CD3BA411ECDF30C0029AA81 /* routing_helpers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = routing_helpers.hpp; sourceTree = ""; }; 342D83381D5233E8000D8AEA /* displacement_mode_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = displacement_mode_manager.cpp; sourceTree = ""; }; 342D83391D5233E8000D8AEA /* displacement_mode_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = displacement_mode_manager.hpp; sourceTree = ""; }; 34583BCD1C88556800F94664 /* place_page_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = place_page_info.cpp; sourceTree = ""; }; @@ -161,8 +163,6 @@ 45580ABC1E2CBD5E00CD535D /* benchmark_tools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = benchmark_tools.cpp; sourceTree = ""; }; 45580ABD1E2CBD5E00CD535D /* benchmark_tools.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = benchmark_tools.hpp; sourceTree = ""; }; 45D287661E966E3400587F05 /* liblocal_ads.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblocal_ads.a; path = "../../../omim-build/xcode/Debug-iphonesimulator/liblocal_ads.a"; sourceTree = ""; }; - 56B6EAF61EA4BAF00037D963 /* mwm_tree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mwm_tree.cpp; sourceTree = ""; }; - 56B6EAF71EA4BAF00037D963 /* mwm_tree.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mwm_tree.hpp; sourceTree = ""; }; 670E393E1C46C5C700E9C0A6 /* gps_tracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gps_tracker.cpp; sourceTree = ""; }; 670E393F1C46C5C700E9C0A6 /* gps_tracker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = gps_tracker.hpp; sourceTree = ""; }; 674231CA1DF984F600913FEB /* libtraffic.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtraffic.a; path = "../../../omim-build/xcode/Debug/libtraffic.a"; sourceTree = ""; }; @@ -442,8 +442,6 @@ 3D47B2901F054BC5000828D2 /* taxi_delegate.cpp */, 3D47B2911F054BC5000828D2 /* taxi_delegate.hpp */, 3D74ABBD1EA76F1D0063A898 /* local_ads_supported_types.cpp */, - 56B6EAF61EA4BAF00037D963 /* mwm_tree.cpp */, - 56B6EAF71EA4BAF00037D963 /* mwm_tree.hpp */, 45580ABD1E2CBD5E00CD535D /* benchmark_tools.hpp */, 45580ABC1E2CBD5E00CD535D /* benchmark_tools.cpp */, F63421F61DF9BF9100A96868 /* reachable_by_taxi_checker.cpp */, @@ -490,6 +488,8 @@ 0C2B73DD1E92AB9900530BB8 /* local_ads_manager.hpp */, 675346051A4054E800A0A8C3 /* mwm_url.cpp */, 675346061A4054E800A0A8C3 /* mwm_url.hpp */, + 0CD3BA401ECDF30C0029AA81 /* routing_helpers.cpp */, + 0CD3BA411ECDF30C0029AA81 /* routing_helpers.hpp */, 6753462C1A4054E800A0A8C3 /* track.cpp */, 6753462D1A4054E800A0A8C3 /* track.hpp */, 6753462E1A4054E800A0A8C3 /* user_mark_container.cpp */, @@ -508,11 +508,11 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 56B6EAF91EA4BAF00037D963 /* mwm_tree.hpp in Headers */, 347B60771DD9926D0050FA24 /* traffic_manager.hpp in Headers */, 6753466B1A4054E800A0A8C3 /* geourl_process.hpp in Headers */, F6B283081C1B03320081957A /* gps_track_storage.hpp in Headers */, 675346671A4054E800A0A8C3 /* ge0_parser.hpp in Headers */, + 0CD3BA431ECDF30C0029AA81 /* routing_helpers.hpp in Headers */, 675346A21A4054E800A0A8C3 /* user_mark.hpp in Headers */, 454649F21F2728CE00EF4064 /* local_ads_mark.hpp in Headers */, F6B283061C1B03320081957A /* gps_track_filter.hpp in Headers */, @@ -650,7 +650,7 @@ 6753469B1A4054E800A0A8C3 /* track.cpp in Sources */, 675346621A4054E800A0A8C3 /* feature_vec_model.cpp in Sources */, 6753469D1A4054E800A0A8C3 /* user_mark_container.cpp in Sources */, - 56B6EAF81EA4BAF00037D963 /* mwm_tree.cpp in Sources */, + 0CD3BA421ECDF30C0029AA81 /* routing_helpers.cpp in Sources */, 674C38621BFF3095000D603B /* user_mark.cpp in Sources */, 675346641A4054E800A0A8C3 /* framework.cpp in Sources */, 454649F11F2728CE00EF4064 /* local_ads_mark.cpp in Sources */,