diff --git a/drape_frontend/user_marks_provider.hpp b/drape_frontend/user_marks_provider.hpp index f809c45a46..15bad85ca1 100644 --- a/drape_frontend/user_marks_provider.hpp +++ b/drape_frontend/user_marks_provider.hpp @@ -105,7 +105,7 @@ public: virtual dp::Color GetColor(size_t layerIndex) const = 0; virtual float GetWidth(size_t layerIndex) const = 0; virtual float GetDepth(size_t layerIndex) const = 0; - virtual std::vector const & GetPoints() const = 0; + virtual std::vector GetPoints() const = 0; private: kml::TrackId m_id; diff --git a/geometry/point_with_altitude.cpp b/geometry/point_with_altitude.cpp index 49f4bd2653..c54af9787e 100644 --- a/geometry/point_with_altitude.cpp +++ b/geometry/point_with_altitude.cpp @@ -16,6 +16,24 @@ PointWithAltitude::PointWithAltitude(m2::PointD const & point, Altitude altitude { } +PointWithAltitude::PointWithAltitude(m2::PointD && point, Altitude altitude) + : m_point(std::move(point)), m_altitude(altitude) +{ +} + +bool PointWithAltitude::operator==(PointWithAltitude const & r) const +{ + return m_point == r.m_point && m_altitude == r.m_altitude; +} + +bool PointWithAltitude::operator<(PointWithAltitude const & r) const +{ + if (m_point != r.m_point) + return m_point < r.m_point; + + return m_altitude < r.m_altitude; +} + std::string DebugPrint(PointWithAltitude const & r) { std::ostringstream ss; @@ -23,6 +41,12 @@ std::string DebugPrint(PointWithAltitude const & r) << "}"; return ss.str(); } + +PointWithAltitude MakePointWithAltitudeForTesting(m2::PointD const & point) +{ + return PointWithAltitude(point, kDefaultAltitudeMeters); +} + bool AlmostEqualAbs(PointWithAltitude const & lhs, PointWithAltitude const & rhs, double eps) { return lhs.GetPoint().EqualDxDy(rhs.GetPoint(), eps) && lhs.GetAltitude() == rhs.GetAltitude(); diff --git a/geometry/point_with_altitude.hpp b/geometry/point_with_altitude.hpp index ab05a6bdee..02ca0b42d9 100644 --- a/geometry/point_with_altitude.hpp +++ b/geometry/point_with_altitude.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace geometry @@ -20,16 +21,19 @@ class PointWithAltitude public: PointWithAltitude(); PointWithAltitude(m2::PointD const & point, Altitude altitude); - PointWithAltitude(PointWithAltitude const &) = default; - PointWithAltitude & operator=(PointWithAltitude const &) = default; + PointWithAltitude(m2::PointD && point, Altitude altitude); - bool operator==(PointWithAltitude const & r) const { return m_point == r.m_point; } + bool operator==(PointWithAltitude const & r) const; bool operator!=(PointWithAltitude const & r) const { return !(*this == r); } - bool operator<(PointWithAltitude const & r) const { return m_point < r.m_point; } + bool operator<(PointWithAltitude const & r) const; m2::PointD const & GetPoint() const { return m_point; } Altitude GetAltitude() const { return m_altitude; } + template + void SetPoint(T && point) { m_point = std::forward(point); } + void SetAltitude(Altitude altitude) { m_altitude = altitude; } + private: friend std::string DebugPrint(PointWithAltitude const & r); @@ -43,10 +47,7 @@ template m2::Point GetPoint(m2::Point const & point) { return point; } inline m2::PointD GetPoint(PointWithAltitude const & pwa) { return pwa.GetPoint(); } -inline PointWithAltitude MakePointWithAltitudeForTesting(m2::PointD const & point) -{ - return PointWithAltitude(point, kDefaultAltitudeMeters); -} +PointWithAltitude MakePointWithAltitudeForTesting(m2::PointD const & point); bool AlmostEqualAbs(PointWithAltitude const & lhs, PointWithAltitude const & rhs, double eps); } // namespace geometry diff --git a/kml/CMakeLists.txt b/kml/CMakeLists.txt index eb66185d7f..63bce7c7e4 100644 --- a/kml/CMakeLists.txt +++ b/kml/CMakeLists.txt @@ -11,6 +11,7 @@ set( type_utils.hpp types.hpp types_v3.hpp + types_v6.hpp visitors.hpp ) diff --git a/kml/kml_tests/serdes_tests.cpp b/kml/kml_tests/serdes_tests.cpp index cc21049120..8b20aa4b74 100644 --- a/kml/kml_tests/serdes_tests.cpp +++ b/kml/kml_tests/serdes_tests.cpp @@ -96,8 +96,9 @@ kml::FileData GenerateKmlFileData() trackData.m_layers = {{6.0, {kml::PredefinedColor::None, 0xff0000ff}}, {7.0, {kml::PredefinedColor::None, 0x00ff00ff}}}; trackData.m_timestamp = std::chrono::system_clock::from_time_t(900); - trackData.m_points = {m2::PointD(45.9242, 56.8679), m2::PointD(45.2244, 56.2786), - m2::PointD(45.1964, 56.9832)}; + trackData.m_pointsWithAltitudes = {{m2::PointD(45.9242, 56.8679), 1}, + {m2::PointD(45.2244, 56.2786), 2}, + {m2::PointD(45.1964, 56.9832), 3}}; trackData.m_visible = false; trackData.m_nearestToponyms = {"12345", "54321", "98765"}; trackData.m_properties = {{"tr_property1", "value1"}, {"tr_property2", "value2"}}; @@ -307,7 +308,7 @@ char const * kGeneratedKml = " 6\n" " \n" " 1970-01-01T00:15:00Z\n" - " 45.9242,49.326859 45.2244,48.941288 45.1964,49.401948\n" + " 45.9242,49.326859,1 45.2244,48.941288,2 45.1964,49.401948,3\n" " \n" " \n" " Тестовый трек\n" diff --git a/kml/kml_tests/tests_data.hpp b/kml/kml_tests/tests_data.hpp index b182f9da11..9698c5d80f 100644 --- a/kml/kml_tests/tests_data.hpp +++ b/kml/kml_tests/tests_data.hpp @@ -1364,7 +1364,7 @@ std::vector const kBinKmlV4 = { }; std::vector const kBinKml = { - 0x06, 0x00, 0x00, 0x1E, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x1E, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, diff --git a/kml/serdes.cpp b/kml/serdes.cpp index 4550b06b45..e1025db868 100644 --- a/kml/serdes.cpp +++ b/kml/serdes.cpp @@ -2,11 +2,12 @@ #include "indexer/classificator.hpp" -#include "geometry/mercator.hpp" - #include "coding/hex.hpp" +#include "coding/point_coding.hpp" #include "coding/string_utf8_multilang.hpp" +#include "geometry/mercator.hpp" + #include "base/assert.hpp" #include "base/stl_helpers.hpp" #include "base/string_utils.hpp" @@ -75,6 +76,11 @@ std::string PointToString(m2::PointD const & org) return ss.str(); } +std::string PointToString(geometry::PointWithAltitude const & pt) +{ + return PointToString(pt.GetPoint()) + "," + strings::to_string(pt.GetAltitude()); +} + std::string GetLocalizableString(LocalizableString const & s, int8_t lang) { auto const it = s.find(lang); @@ -495,10 +501,10 @@ void SaveTrackData(KmlWriter::WriterWrapper & writer, TrackData const & trackDat } writer << kIndent4 << ""; - for (size_t i = 0; i < trackData.m_points.size(); ++i) + for (size_t i = 0; i < trackData.m_pointsWithAltitudes.size(); ++i) { - writer << PointToString(trackData.m_points[i]); - if (i + 1 != trackData.m_points.size()) + writer << PointToString(trackData.m_pointsWithAltitudes[i]); + if (i + 1 != trackData.m_pointsWithAltitudes.size()) writer << " "; } writer << "\n"; @@ -507,6 +513,50 @@ void SaveTrackData(KmlWriter::WriterWrapper & writer, TrackData const & trackDat writer << kIndent2 << "\n"; } + +bool ParsePoint(std::string const & s, char const * delim, m2::PointD & pt, + geometry::Altitude & altitude) +{ + // Order in string is: lon, lat, z. + strings::SimpleTokenizer iter(s, delim); + if (!iter) + return false; + + double lon; + if (strings::to_double(*iter, lon) && mercator::ValidLon(lon) && ++iter) + { + double lat; + if (strings::to_double(*iter, lat) && mercator::ValidLat(lat)) + { + pt = mercator::FromLatLon(lat, lon); + + int rawAltitude; + if (++iter && strings::to_int(*iter, rawAltitude)) + altitude = static_cast(rawAltitude); + + return true; + } + } + return false; +} + +bool ParsePoint(std::string const & s, char const * delim, m2::PointD & pt) +{ + geometry::Altitude dummyAltitude; + return ParsePoint(s, delim, pt, dummyAltitude); +} + +bool ParsePointWithAltitude(std::string const & s, char const * delim, + geometry::PointWithAltitude & point) +{ + geometry::Altitude altitude = geometry::kDefaultAltitudeMeters; + m2::PointD pt; + auto result = ParsePoint(s, delim, pt, altitude); + point.SetPoint(std::move(pt)); + point.SetAltitude(altitude); + + return result; +} } // namespace KmlWriter::WriterWrapper & KmlWriter::WriterWrapper::operator<<(std::string const & str) @@ -566,30 +616,10 @@ void KmlParser::ResetPoint() m_trackWidth = kDefaultTrackWidth; m_icon = BookmarkIcon::None; - m_points.clear(); + m_pointsWithAltitudes.clear(); m_geometryType = GEOMETRY_TYPE_UNKNOWN; } -bool KmlParser::ParsePoint(std::string const & s, char const * delim, m2::PointD & pt) -{ - // Order in string is: lon, lat, z. - strings::SimpleTokenizer iter(s, delim); - if (!iter) - return false; - - double lon; - if (strings::to_double(*iter, lon) && mercator::ValidLon(lon) && ++iter) - { - double lat; - if (strings::to_double(*iter, lat) && mercator::ValidLat(lat)) - { - pt = mercator::FromLatLon(lat, lon); - return true; - } - } - return false; -} - void KmlParser::SetOrigin(std::string const & s) { m_geometryType = GEOMETRY_TYPE_POINT; @@ -607,11 +637,14 @@ void KmlParser::ParseLineCoordinates(std::string const & s, char const * blockSe strings::SimpleTokenizer tupleIter(s, blockSeparator); while (tupleIter) { - m2::PointD pt; - if (ParsePoint(*tupleIter, coordSeparator, pt)) + geometry::PointWithAltitude point; + if (ParsePointWithAltitude(*tupleIter, coordSeparator, point)) { - if (m_points.empty() || !pt.EqualDxDy(m_points.back(), 1e-5 /* eps */)) - m_points.push_back(std::move(pt)); + if (m_pointsWithAltitudes.empty() || + !AlmostEqualAbs(m_pointsWithAltitudes.back(), point, kMwmPointAccuracy)) + { + m_pointsWithAltitudes.emplace_back(std::move(point)); + } } ++tupleIter; } @@ -637,7 +670,7 @@ bool KmlParser::MakeValid() } else if (GEOMETRY_TYPE_LINE == m_geometryType) { - return m_points.size() > 1; + return m_pointsWithAltitudes.size() > 1; } return false; @@ -760,7 +793,7 @@ void KmlParser::Pop(std::string const & tag) data.m_description = std::move(m_description); data.m_layers = std::move(m_trackLayers); data.m_timestamp = m_timestamp; - data.m_points = m_points; + data.m_pointsWithAltitudes = m_pointsWithAltitudes; data.m_visible = m_visible; data.m_nearestToponyms = std::move(m_nearestToponyms); data.m_properties = std::move(m_properties); diff --git a/kml/serdes.hpp b/kml/serdes.hpp index 4343822ef7..ec13e8bd47 100644 --- a/kml/serdes.hpp +++ b/kml/serdes.hpp @@ -6,6 +6,9 @@ #include "coding/reader.hpp" #include "coding/writer.hpp" +#include "geometry/point2d.hpp" +#include "geometry/point_with_altitude.hpp" + #include "base/exception.hpp" #include "base/stl_helpers.hpp" @@ -83,7 +86,6 @@ private: }; void ResetPoint(); - bool ParsePoint(std::string const & s, char const * delim, m2::PointD & pt); void SetOrigin(std::string const & s); void ParseLineCoordinates(std::string const & s, char const * blockSeparator, char const * coordSeparator); @@ -96,7 +98,7 @@ private: std::vector m_tags; GeometryType m_geometryType; - std::vector m_points; + std::vector m_pointsWithAltitudes; uint32_t m_color; std::string m_styleId; diff --git a/kml/serdes_binary.hpp b/kml/serdes_binary.hpp index 7ee94ad48c..1980a891f9 100644 --- a/kml/serdes_binary.hpp +++ b/kml/serdes_binary.hpp @@ -3,14 +3,15 @@ #include "kml/header_binary.hpp" #include "kml/types.hpp" #include "kml/types_v3.hpp" +#include "kml/types_v6.hpp" #include "kml/visitors.hpp" +#include "platform/platform.hpp" + #include "coding/read_write_utils.hpp" #include "coding/sha1.hpp" #include "coding/text_storage.hpp" -#include "platform/platform.hpp" - #include #include @@ -21,13 +22,16 @@ namespace binary enum class Version : uint8_t { V0 = 0, - V1 = 1, // 11th April 2018 (new Point2D storage, added deviceId, feature name -> custom name). - V2 = 2, // 25th April 2018 (added serverId). - V3 = 3, // 7th May 2018 (persistent feature types). - V4 = 4, // 26th August 2019 (key-value properties and nearestToponym for bookmarks and tracks, cities -> toponyms). - V5 = 5, // 21st November 2019 (extended color palette). - V6 = 6, // 3rd December 2019 (extended bookmark icons). - Latest = V6 + V1 = 1, // 11th April 2018: new Point2D storage, added deviceId, feature name -> custom name. + V2 = 2, // 25th April 2018: added serverId. + V3 = 3, // 7th May 2018: persistent feature types. V3 is binary compatible with lower versions. + V4 = 4, // 26th August 2019: key-value properties and nearestToponym for bookmarks and tracks, + // cities -> toponyms. + V5 = 5, // 21st November 2019: extended color palette. + V6 = 6, // 3rd December 2019: extended bookmark icons. V6 is binary compatible with V4 and V5 + // versions. + V7 = 7, // 13th February 2020: track points are replaced by points with altitude. + Latest = V7 }; class SerializerKml @@ -140,7 +144,7 @@ public: auto const v = ReadPrimitiveFromSource(source); if (v != Version::Latest && v != Version::V2 && v != Version::V3 && v != Version::V4 && - v != Version::V5) + v != Version::V5 && v != Version::V6) { MYTHROW(DeserializeException, ("Incorrect file version.")); } @@ -152,13 +156,19 @@ public: auto subReader = reader.CreateSubReader(source.Pos(), source.Size()); InitializeIfNeeded(*subReader); - if (v == Version::V6 || v == Version::V5 || v == Version::V4) + if (v == Version::V7) + { + DeserializeFileData(subReader, m_data); + } + else if (v == Version::V6 || v == Version::V5 || v == Version::V4) { // NOTE: v.4, v.5 and v.6 are binary compatible. - DeserializeCategory(subReader, m_data); - DeserializeBookmarks(subReader, m_data); - DeserializeTracks(subReader, m_data); - DeserializeStrings(subReader, m_data); + FileDataV6 dataV6; + dataV6.m_deviceId = m_data.m_deviceId; + dataV6.m_serverId = m_data.m_serverId; + DeserializeFileData(subReader, dataV6); + + m_data = dataV6.ConvertToLatestVersion(); } else { @@ -166,16 +176,12 @@ public: FileDataV3 dataV3; dataV3.m_deviceId = m_data.m_deviceId; dataV3.m_serverId = m_data.m_serverId; - DeserializeCategory(subReader, dataV3); - DeserializeBookmarks(subReader, dataV3); + DeserializeFileData(subReader, dataV3); // Migrate bookmarks (it's necessary ony for v.2). if (v == Version::V2) MigrateBookmarksV2(dataV3); - DeserializeTracks(subReader, dataV3); - DeserializeStrings(subReader, dataV3); - m_data = dataV3.ConvertToLatestVersion(); } } @@ -247,6 +253,18 @@ private: MYTHROW(DeserializeException, ("Incorrect double bits count: ", m_doubleBits)); } + template + void DeserializeFileData(std::unique_ptr & subReader, FileDataType & data) + { + // Keep in mind - deserialization/serialization works in two stages: + // - serialization/deserialization non-string members of structures; + // - serialization/deserialization string members of structures. + DeserializeCategory(subReader, data); + DeserializeBookmarks(subReader, data); + DeserializeTracks(subReader, data); + DeserializeStrings(subReader, data); + } + template void DeserializeCategory(std::unique_ptr & subReader, FileDataType & data) { @@ -288,8 +306,7 @@ private: DeserializedStringCollector collector(strings); CollectorVisitor visitor(collector); data.Visit(visitor); - CollectorVisitor clearVisitor(collector, - true /* clear index */); + CollectorVisitor clearVisitor(collector, true /* clear index */); data.Visit(clearVisitor); } diff --git a/kml/type_utils.cpp b/kml/type_utils.cpp index dd61e9b3d7..eb7aa06e5d 100644 --- a/kml/type_utils.cpp +++ b/kml/type_utils.cpp @@ -1,7 +1,6 @@ #include "kml/type_utils.hpp" #include "kml/types.hpp" -#include "indexer/categories_holder.hpp" #include "indexer/classificator.hpp" #include "indexer/feature_utils.hpp" @@ -17,10 +16,24 @@ bool IsEqual(std::vector const & v1, std::vector const & if (v1.size() != v2.size()) return false; - double constexpr kEps = 1e-5; for (size_t i = 0; i < v1.size(); ++i) { - if (!v1[i].EqualDxDy(v2[i], kEps)) + if (!v1[i].EqualDxDy(v2[i], kMwmPointAccuracy)) + return false; + } + + return true; +} + +bool IsEqual(std::vector const & v1, + std::vector const & v2) +{ + if (v1.size() != v2.size()) + return false; + + for (size_t i = 0; i < v1.size(); ++i) + { + if (!AlmostEqualAbs(v1[i], v2[i], kMwmPointAccuracy)) return false; } diff --git a/kml/type_utils.hpp b/kml/type_utils.hpp index b87961820d..659fd538ae 100644 --- a/kml/type_utils.hpp +++ b/kml/type_utils.hpp @@ -3,16 +3,17 @@ #include "indexer/feature.hpp" #include "geometry/point2d.hpp" +#include "geometry/point_with_altitude.hpp" -#include #include +#include #include #include #include #include #include -#include #include +#include namespace kml { @@ -82,6 +83,8 @@ inline void SetDefaultStr(LocalizableString & localizableStr, std::string const } extern bool IsEqual(std::vector const & v1, std::vector const & v2); +extern bool IsEqual(std::vector const & v1, + std::vector const & v2); struct BookmarkData; std::string GetPreferredBookmarkName(BookmarkData const & bmData, std::string const & languageOrig); diff --git a/kml/types.hpp b/kml/types.hpp index e06ad84e07..e2a19c0f6f 100644 --- a/kml/types.hpp +++ b/kml/types.hpp @@ -2,6 +2,8 @@ #include "kml/type_utils.hpp" +#include "coding/point_coding.hpp" + #include "base/visitor.hpp" #include @@ -206,13 +208,12 @@ struct BookmarkData bool operator==(BookmarkData const & data) const { - double constexpr kEps = 1e-5; return m_id == data.m_id && m_name == data.m_name && m_description == data.m_description && m_color == data.m_color && m_icon == data.m_icon && m_viewportScale == data.m_viewportScale && IsEqual(m_timestamp, data.m_timestamp) && - m_point.EqualDxDy(data.m_point, kEps) && + m_point.EqualDxDy(data.m_point, kMwmPointAccuracy) && m_featureTypes == data.m_featureTypes && m_customName == data.m_customName && m_boundTracks == data.m_boundTracks && @@ -281,7 +282,7 @@ struct TrackData visitor(m_description, "description"), visitor(m_layers, "layers"), visitor(m_timestamp, "timestamp"), - visitor(m_points, "points"), + visitor(m_pointsWithAltitudes, "pointsWithAltitudes"), visitor(m_visible, "visible"), visitor(m_nearestToponyms, "nearestToponyms"), visitor(m_properties, "properties"), @@ -293,7 +294,8 @@ struct TrackData { return m_id == data.m_id && m_localId == data.m_localId && m_name == data.m_name && m_description == data.m_description && m_layers == data.m_layers && - IsEqual(m_timestamp, data.m_timestamp) && IsEqual(m_points, data.m_points) && + IsEqual(m_timestamp, data.m_timestamp) && + IsEqual(m_pointsWithAltitudes, data.m_pointsWithAltitudes) && m_visible == data.m_visible && m_nearestToponyms == data.m_nearestToponyms && m_properties == data.m_properties; } @@ -312,8 +314,8 @@ struct TrackData std::vector m_layers; // Creation timestamp. Timestamp m_timestamp = {}; - // Points. - std::vector m_points; + // Points with altitudes. + std::vector m_pointsWithAltitudes; // Visibility. bool m_visible = true; // Nearest toponyms. diff --git a/kml/types_v3.hpp b/kml/types_v3.hpp index 01399336c2..d19fb4e6db 100644 --- a/kml/types_v3.hpp +++ b/kml/types_v3.hpp @@ -9,8 +9,10 @@ #include #include #include +#include #include +// V3 kml structures are used to deserialize data of lower versions (V0 - V3). namespace kml { struct BookmarkDataV3 @@ -118,7 +120,8 @@ struct TrackDataV3 data.m_description = m_description; data.m_layers = m_layers; data.m_timestamp = m_timestamp; - data.m_points = m_points; + for (auto & pt : m_points) + data.m_pointsWithAltitudes.emplace_back(std::move(pt), geometry::kDefaultAltitudeMeters); return data; } diff --git a/kml/types_v6.hpp b/kml/types_v6.hpp new file mode 100644 index 0000000000..aa30bf4720 --- /dev/null +++ b/kml/types_v6.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include "kml/type_utils.hpp" +#include "kml/types.hpp" + +#include "base/visitor.hpp" + +#include +#include +#include +#include +#include +#include + +namespace kml +{ +// All kml structures for V6 and V7 are same except TrackData. +// Saved V6 version of TrackData to support migration from V6 to V7. +struct TrackDataV6 +{ + DECLARE_VISITOR_AND_DEBUG_PRINT(TrackDataV6, visitor(m_id, "id"), + visitor(m_localId, "localId"), + visitor(m_name, "name"), + visitor(m_description, "description"), + visitor(m_layers, "layers"), + visitor(m_timestamp, "timestamp"), + visitor(m_points, "points"), + visitor(m_visible, "visible"), + visitor(m_nearestToponyms, "nearestToponyms"), + visitor(m_properties, "properties"), + VISITOR_COLLECTABLE) + + DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description, m_nearestToponyms, m_properties) + + bool operator==(TrackDataV6 const & data) const + { + return m_id == data.m_id && m_localId == data.m_localId && m_name == data.m_name && + m_description == data.m_description && m_layers == data.m_layers && + IsEqual(m_timestamp, data.m_timestamp) && IsEqual(m_points, data.m_points) && + m_visible == data.m_visible && m_nearestToponyms == data.m_nearestToponyms && + m_properties == data.m_properties; + } + + bool operator!=(TrackDataV6 const & data) const { return !operator==(data); } + + TrackData ConvertToLatestVersion() const + { + TrackData data; + data.m_id = m_id; + data.m_localId = m_localId; + data.m_name = m_name; + data.m_description = m_description; + data.m_layers = m_layers; + data.m_timestamp = m_timestamp; + for (auto & pt : m_points) + data.m_pointsWithAltitudes.emplace_back(std::move(pt), geometry::kDefaultAltitudeMeters); + data.m_visible = m_visible; + data.m_nearestToponyms = m_nearestToponyms; + data.m_properties = m_properties; + + return data; + } + + // Unique id (it will not be serialized in text files). + TrackId m_id = kInvalidTrackId; + // Local track id. + LocalId m_localId = 0; + // Track's name. + LocalizableString m_name; + // Track's description. + LocalizableString m_description; + // Layers. + std::vector m_layers; + // Creation timestamp. + Timestamp m_timestamp = {}; + // Points. + std::vector m_points; + // Visibility. + bool m_visible = true; + // Nearest toponyms. + std::vector m_nearestToponyms; + // Key-value properties. + Properties m_properties; +}; + +struct FileDataV6 +{ + DECLARE_VISITOR_AND_DEBUG_PRINT(FileDataV6, visitor(m_serverId, "serverId"), + visitor(m_categoryData, "category"), + visitor(m_bookmarksData, "bookmarks"), + visitor(m_tracksData, "tracks")) + + bool operator==(FileDataV6 const & data) const + { + return m_serverId == data.m_serverId && m_categoryData == data.m_categoryData && + m_bookmarksData == data.m_bookmarksData && m_tracksData == data.m_tracksData; + } + + bool operator!=(FileDataV6 const & data) const { return !operator==(data); } + + FileData ConvertToLatestVersion() const + { + FileData data; + data.m_deviceId = m_deviceId; + data.m_serverId = m_serverId; + data.m_categoryData = m_categoryData; + data.m_bookmarksData = m_bookmarksData; + + data.m_tracksData.reserve(m_tracksData.size()); + for (auto const & track : m_tracksData) + data.m_tracksData.emplace_back(track.ConvertToLatestVersion()); + + return data; + } + + // Device id (it will not be serialized in text files). + std::string m_deviceId; + // Server id. + std::string m_serverId; + // Category's data. + CategoryData m_categoryData; + // Bookmarks collection. + std::vector m_bookmarksData; + // Tracks collection. + std::vector m_tracksData; +}; +} // namespace kml diff --git a/kml/visitors.hpp b/kml/visitors.hpp index fa51436e05..fbb17f3a01 100644 --- a/kml/visitors.hpp +++ b/kml/visitors.hpp @@ -8,6 +8,7 @@ #include "coding/varint.hpp" #include "geometry/mercator.hpp" +#include "geometry/point_with_altitude.hpp" #include "base/bits.hpp" @@ -42,7 +43,8 @@ class CollectorVisitor std::is_same::value || std::is_same::value || std::is_same::value || - std::is_same::value}; + std::is_same::value || + std::is_same::value}; }; public: @@ -343,6 +345,12 @@ public: WritePointD(m_sink, pt, m_doubleBits); } + void operator()(geometry::PointWithAltitude const & pt, char const * /* name */ = nullptr) + { + WritePointD(m_sink, pt.GetPoint(), m_doubleBits); + WriteVarInt(m_sink, pt.GetAltitude()); + } + void operator()(double d, char const * /* name */ = nullptr) { auto const encoded = DoubleToUint32(d, kMinLineWidth, kMaxLineWidth, m_doubleBits); @@ -384,6 +392,25 @@ public: } } + void operator()(std::vector const & points, + char const * /* name */ = nullptr) + { + WriteVarUint(m_sink, static_cast(points.size())); + m2::PointU lastUpt = m2::PointU::Zero(); + for (auto const & point : points) + { + auto const upt = PointDToPointU(point.GetPoint(), m_doubleBits); + coding::EncodePointDelta(m_sink, lastUpt, upt); + lastUpt = upt; + } + geometry::Altitude lastAltitude = geometry::kDefaultAltitudeMeters; + for (auto const & point : points) + { + WriteVarInt(m_sink, point.GetAltitude() - lastAltitude); + lastAltitude = point.GetAltitude(); + } + } + template std::enable_if_t::value> operator()(D d, char const * /* name */ = nullptr) @@ -513,6 +540,12 @@ public: pt = ReadPointD(m_source, m_doubleBits); } + void operator()(geometry::PointWithAltitude & pt, char const * /* name */ = nullptr) + { + pt.SetPoint(ReadPointD(m_source, m_doubleBits)); + pt.SetAltitude(ReadVarInt(m_source)); + } + void operator()(double & d, char const * /* name */ = nullptr) { auto const v = ReadVarUint(m_source); @@ -564,6 +597,25 @@ public: } } + void operator()(std::vector & points, + char const * /* name */ = nullptr) + { + auto const sz = ReadVarUint(m_source); + points.reserve(sz); + m2::PointU lastUpt = m2::PointU::Zero(); + for (uint32_t i = 0; i < sz; ++i) + { + lastUpt = coding::DecodePointDelta(m_source, lastUpt); + points.emplace_back(PointUToPointD(lastUpt, m_doubleBits), geometry::kDefaultAltitudeMeters); + } + geometry::Altitude lastAltitude = geometry::kDefaultAltitudeMeters; + for (auto & point : points) + { + point.SetAltitude(lastAltitude + ReadVarInt(m_source)); + lastAltitude = point.GetAltitude(); + } + } + template std::enable_if_t::value> operator()(D & d, char const * /* name */ = nullptr) diff --git a/map/map_tests/bookmarks_test.cpp b/map/map_tests/bookmarks_test.cpp index 0989a2d39e..fcd1d579c9 100644 --- a/map/map_tests/bookmarks_test.cpp +++ b/map/map_tests/bookmarks_test.cpp @@ -800,7 +800,7 @@ UNIT_TEST(Bookmarks_Sorting) { kml::TrackData trackData; trackData.m_id = testTrackData.m_trackId; - trackData.m_points = {{0.0, 0.0}, {1.0, 0.0}}; + trackData.m_pointsWithAltitudes = {{{0.0, 0.0}, 1}, {{1.0, 0.0}, 2}}; if (testTrackData.m_hoursSinceCreation != kUnknownTime) trackData.m_timestamp = currentTime - testTrackData.m_hoursSinceCreation; auto const * track = es.CreateTrack(std::move(trackData)); diff --git a/map/track.cpp b/map/track.cpp index ec4ab00611..325fce8094 100644 --- a/map/track.cpp +++ b/map/track.cpp @@ -14,7 +14,7 @@ Track::Track(kml::TrackData && data) , m_groupID(0) { m_data.m_id = GetId(); - ASSERT_GREATER(m_data.m_points.size(), 1, ()); + ASSERT_GREATER(m_data.m_pointsWithAltitudes.size(), 1, ()); } std::string Track::GetName() const @@ -25,8 +25,8 @@ std::string Track::GetName() const m2::RectD Track::GetLimitRect() const { m2::RectD rect; - for (auto const & point : m_data.m_points) - rect.Add(point); + for (auto const & point : m_data.m_pointsWithAltitudes) + rect.Add(point.GetPoint()); return rect; } @@ -34,13 +34,13 @@ double Track::GetLengthMeters() const { double res = 0.0; - auto it = m_data.m_points.begin(); - double lat1 = mercator::YToLat(it->y); - double lon1 = mercator::XToLon(it->x); - for (++it; it != m_data.m_points.end(); ++it) + auto it = m_data.m_pointsWithAltitudes.begin(); + double lat1 = mercator::YToLat(it->GetPoint().y); + double lon1 = mercator::XToLon(it->GetPoint().x); + for (++it; it != m_data.m_pointsWithAltitudes.end(); ++it) { - double const lat2 = mercator::YToLat(it->y); - double const lon2 = mercator::XToLon(it->x); + double const lat2 = mercator::YToLat(it->GetPoint().y); + double const lon2 = mercator::XToLon(it->GetPoint().x); res += ms::DistanceOnEarth(lat1, lon1, lat2, lon2); lat1 = lat2; lon1 = lon2; @@ -76,9 +76,18 @@ float Track::GetDepth(size_t layerIndex) const return layerIndex * 10; } -std::vector const & Track::GetPoints() const +std::vector Track::GetPoints() const { - return m_data.m_points; + std::vector result; + for (auto const & pt : m_data.m_pointsWithAltitudes) + result.push_back(pt.GetPoint()); + + return result; +} + +std::vector const & Track::GetPointsWithAltitudes() const +{ + return m_data.m_pointsWithAltitudes; } void Track::Attach(kml::MarkGroupId groupId) diff --git a/map/track.hpp b/map/track.hpp index 14538af16e..80432c01c0 100644 --- a/map/track.hpp +++ b/map/track.hpp @@ -29,7 +29,8 @@ public: dp::Color GetColor(size_t layerIndex) const override; float GetWidth(size_t layerIndex) const override; float GetDepth(size_t layerIndex) const override; - std::vector const & GetPoints() const override; + std::vector GetPoints() const override; + std::vector const & GetPointsWithAltitudes() const; void Attach(kml::MarkGroupId groupId); void Detach(); diff --git a/routing/routing_quality/routing_quality_tool/utils.cpp b/routing/routing_quality/routing_quality_tool/utils.cpp index 4bb9a1f541..421a0cc964 100644 --- a/routing/routing_quality/routing_quality_tool/utils.cpp +++ b/routing/routing_quality/routing_quality_tool/utils.cpp @@ -1,5 +1,3 @@ -#include - #include "routing/routing_quality/routing_quality_tool/utils.hpp" #include "routing/vehicle_mask.hpp" @@ -9,11 +7,14 @@ #include "coding/string_utf8_multilang.hpp" +#include "geometry/point_with_altitude.hpp" + #include "base/assert.hpp" #include "base/file_name_utils.hpp" #include "base/logging.hpp" #include +#include using namespace routing::routes_builder; @@ -32,11 +33,13 @@ void PrintWithSpaces(std::string const & str, size_t maxN) std::cout << " "; } -std::vector ConvertToPointDVector(std::vector const & latlons) +std::vector ConvertToPointsWithAltitudes( + std::vector const & latlons) { - std::vector result(latlons.size()); + std::vector result; + result.reserve(latlons.size()); for (size_t i = 0; i < latlons.size(); ++i) - result[i] = mercator::FromLatLon(latlons[i]); + result.emplace_back(mercator::FromLatLon(latlons[i]), geometry::kDefaultAltitudeMeters); return result; } @@ -90,14 +93,19 @@ void SaveKmlFileDataTo(RoutesBuilder::Result const & mapsmeResult, kml.m_bookmarksData.emplace_back(CreateBookmark(start, true /* isStart */)); kml.m_bookmarksData.emplace_back(CreateBookmark(finish, false /* isStart */)); + auto & resultPoints = + mapsmeResult.GetRoutes().back().m_followedPolyline.GetPolyline().GetPoints(); kml::TrackData mapsmeTrack; - mapsmeTrack.m_points = mapsmeResult.GetRoutes().back().m_followedPolyline.GetPolyline().GetPoints(); + mapsmeTrack.m_pointsWithAltitudes.reserve(resultPoints.size()); + for (auto const & pt : resultPoints) + mapsmeTrack.m_pointsWithAltitudes.emplace_back(pt, geometry::kDefaultAltitudeMeters); + addTrack(std::move(mapsmeTrack)); for (auto const & route : apiResult.GetRoutes()) { kml::TrackData apiTrack; - apiTrack.m_points = ConvertToPointDVector(route.GetWaypoints()); + apiTrack.m_pointsWithAltitudes = ConvertToPointsWithAltitudes(route.GetWaypoints()); addTrack(std::move(apiTrack)); } diff --git a/track_generator/utils.cpp b/track_generator/utils.cpp index 24da750d6f..8c057235d7 100644 --- a/track_generator/utils.cpp +++ b/track_generator/utils.cpp @@ -1,20 +1,21 @@ #include "track_generator/utils.hpp" +#include "routing/routes_builder/routes_builder.hpp" + +#include "routing/base/followed_polyline.hpp" +#include "routing/route.hpp" +#include "routing/routing_callbacks.hpp" + #include "kml/serdes.hpp" #include "kml/type_utils.hpp" #include "kml/types.hpp" -#include "routing/routes_builder/routes_builder.hpp" - -#include "routing/route.hpp" -#include "routing/routing_callbacks.hpp" - -#include "routing/base/followed_polyline.hpp" +#include "platform/platform.hpp" #include "coding/file_reader.hpp" #include "coding/file_writer.hpp" -#include "platform/platform.hpp" +#include "geometry/point_with_altitude.hpp" #include "base/assert.hpp" #include "base/file_name_utils.hpp" @@ -28,10 +29,10 @@ using namespace std; namespace { -vector GetTrackPoints(std::vector const & routePoints) +vector GetTrackPoints(std::vector const & routePoints) { auto const size = routePoints.size(); - vector result; + vector result; result.reserve(size); for (size_t i = 0; i < size;) @@ -66,7 +67,7 @@ vector GetTrackPoints(std::vector const & routePoints) /// * /// Check if there are two perpendicular fake segments and get rid from both of them. - if (!result.empty() && result.back() == routePoints[j]) + if (!result.empty() && result.back().GetPoint() == routePoints[j]) { result.pop_back(); ++j; @@ -74,7 +75,7 @@ vector GetTrackPoints(std::vector const & routePoints) } else { - result.emplace_back(routePoints[i]); + result.emplace_back(routePoints[i], geometry::kDefaultAltitudeMeters); } i = j; @@ -128,7 +129,10 @@ void GenerateTracks(string const & inputDir, string const & outputDir, routing:: numberOfTracks += data.m_tracksData.size(); for (auto & track : data.m_tracksData) { - auto waypoints = track.m_points; + std::vector waypoints; + for (auto const & pt : track.m_pointsWithAltitudes) + waypoints.push_back(pt.GetPoint()); + routing::routes_builder::RoutesBuilder::Params params(type, move(waypoints)); auto result = routesBuilder.ProcessTask(params); @@ -139,7 +143,8 @@ void GenerateTracks(string const & inputDir, string const & outputDir, routing:: continue; } - track.m_points = GetTrackPoints(result.GetRoutes().back().m_followedPolyline.GetPolyline().GetPoints()); + track.m_pointsWithAltitudes = + GetTrackPoints(result.GetRoutes().back().m_followedPolyline.GetPolyline().GetPoints()); } try