From f7aa1ae5f9d8cccbd2c3b8f2678d13b043274425 Mon Sep 17 00:00:00 2001 From: Anatoliy Tomilov Date: Tue, 22 Sep 2020 20:35:46 +0500 Subject: [PATCH] [kml] [kmb] Support 10.3 kml types (V8) in text kml and kmb serdes. MAPSME-14910 --- kml/header_binary.hpp | 28 ++ kml/kml_tests/serdes_tests.cpp | 591 ++++++++++++++++++++------------- kml/kml_tests/tests_data.hpp | 81 ++--- kml/serdes.cpp | 239 +++++++++---- kml/serdes.hpp | 2 + kml/serdes_binary.hpp | 77 +++-- kml/types.hpp | 16 +- kml/visitors.hpp | 22 +- 8 files changed, 677 insertions(+), 379 deletions(-) diff --git a/kml/header_binary.hpp b/kml/header_binary.hpp index d34daa3cc9..12e0503592 100644 --- a/kml/header_binary.hpp +++ b/kml/header_binary.hpp @@ -10,6 +10,23 @@ namespace kml { 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. 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. + V8 = 8, // 24 September 2020: add compilations to types and corresponding section to kmb and + // tags to kml + Latest = V8 +}; + struct Header { template @@ -18,7 +35,11 @@ struct Header visitor(m_categoryOffset, "categoryOffset"); visitor(m_bookmarksOffset, "bookmarksOffset"); visitor(m_tracksOffset, "tracksOffset"); + if (HasCompilationsSection()) + visitor(m_compilationsOffset, "compilationsOffset"); visitor(m_stringsOffset, "stringsOffset"); + if (!HasCompilationsSection()) + m_compilationsOffset = m_stringsOffset; visitor(m_eosOffset, "eosOffset"); } @@ -44,9 +65,16 @@ struct Header return visitor.m_size; } + bool HasCompilationsSection() const + { + return static_cast(m_version) > static_cast(Version::V7); + } + + Version m_version = Version::Latest; uint64_t m_categoryOffset = 0; uint64_t m_bookmarksOffset = 0; uint64_t m_tracksOffset = 0; + uint64_t m_compilationsOffset = 0; uint64_t m_stringsOffset = 0; uint64_t m_eosOffset = 0; }; diff --git a/kml/kml_tests/serdes_tests.cpp b/kml/kml_tests/serdes_tests.cpp index 4c286c1f32..f6f5aff8c8 100644 --- a/kml/kml_tests/serdes_tests.cpp +++ b/kml/kml_tests/serdes_tests.cpp @@ -84,7 +84,10 @@ kml::FileData GenerateKmlFileData() bookmarkData.m_boundTracks = {0}; bookmarkData.m_visible = false; bookmarkData.m_nearestToponym = "12345"; - bookmarkData.m_properties = {{"bm_property1", "value1"}, {"bm_property2", "value2"}}; + bookmarkData.m_properties = {{"bm_property1", "value1"}, + {"bm_property2", "value2"}, + {"score", "5"}, + {"compilations", {"1, 4"}}}; result.m_bookmarksData.emplace_back(std::move(bookmarkData)); kml::TrackData trackData; @@ -104,241 +107,365 @@ kml::FileData GenerateKmlFileData() trackData.m_properties = {{"tr_property1", "value1"}, {"tr_property2", "value2"}}; result.m_tracksData.emplace_back(std::move(trackData)); + kml::CategoryData compilationData1; + compilationData1.m_compilationId = 1; + compilationData1.m_type = kml::CompilationType::Collection; + compilationData1.m_name[kDefaultLang] = "Test collection"; + compilationData1.m_name[kRuLang] = "Тестовая коллекция"; + compilationData1.m_description[kDefaultLang] = "Test collection description"; + compilationData1.m_description[kRuLang] = "Тестовое описание коллекции"; + compilationData1.m_annotation[kDefaultLang] = "Test collection annotation"; + compilationData1.m_annotation[kEnLang] = "Test collection annotation"; + compilationData1.m_imageUrl = "https://localhost/1234.png"; + compilationData1.m_visible = true; + compilationData1.m_authorName = "Maps.Me"; + compilationData1.m_authorId = "54321"; + compilationData1.m_rating = 5.9; + compilationData1.m_reviewsNumber = 333; + compilationData1.m_lastModified = std::chrono::system_clock::from_time_t(999); + compilationData1.m_accessRules = kml::AccessRules::Public; + compilationData1.m_tags = {"mountains", "ski"}; + compilationData1.m_toponyms = {"8", "9"}; + compilationData1.m_languageCodes = {1, 2, 8}; + compilationData1.m_properties = {{"property1", "value1"}, {"property2", "value2"}}; + result.m_compilationData.push_back(std::move(compilationData1)); + + kml::CategoryData compilationData2; + compilationData2.m_compilationId = 4; + compilationData2.m_type = kml::CompilationType::Category; + compilationData2.m_name[kDefaultLang] = "Test category"; + compilationData2.m_name[kRuLang] = "Тестовая категория"; + compilationData2.m_description[kDefaultLang] = "Test category description"; + compilationData2.m_description[kRuLang] = "Тестовое описание категории"; + compilationData2.m_annotation[kDefaultLang] = "Test category annotation"; + compilationData2.m_annotation[kEnLang] = "Test category annotation"; + compilationData2.m_imageUrl = "https://localhost/134.png"; + compilationData2.m_visible = false; + compilationData2.m_authorName = "Maps.Me"; + compilationData2.m_authorId = "11111"; + compilationData2.m_rating = 3.3; + compilationData2.m_reviewsNumber = 222; + compilationData2.m_lastModified = std::chrono::system_clock::from_time_t(323); + compilationData2.m_accessRules = kml::AccessRules::Public; + compilationData2.m_tags = {"mountains", "bike"}; + compilationData2.m_toponyms = {"10", "11"}; + compilationData2.m_languageCodes = {1, 2, 8}; + compilationData2.m_properties = {{"property1", "value1"}, {"property2", "value2"}}; + result.m_compilationData.push_back(std::move(compilationData2)); + return result; } char const * kGeneratedKml = - "\n" - "\n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " Test category\n" - " Test description\n" - " 1\n" - " \n" - " AAAA-BBBB-CCCC-DDDD\n" - " \n" - " Тестовая категория\n" - " Test category\n" - " \n" - " \n" - " Test annotation\n" - " Test annotation\n" - " \n" - " \n" - " Тестовое описание\n" - " Test description\n" - " \n" - " https://localhost/123.png\n" - " Maps.Me\n" - " 1970-01-01T00:16:40Z\n" - " 8.9\n" - " 567\n" - " Public\n" - " \n" - " mountains\n" - " ski\n" - " snowboard\n" - " \n" - " \n" - " 12345\n" - " 54321\n" - " \n" - " \n" - " en\n" - " ja\n" - " ru\n" - " \n" - " \n" - " value1\n" - " value2\n" - " \n" - " \n" - " \n" - " Мое любимое место\n" - " Test bookmark description\n" - " 1970-01-01T00:13:20Z\n" - " #placemark-blue\n" - " 45.9242,49.326859\n" - " \n" - " \n" - " Тестовая метка\n" - " Test bookmark\n" - " \n" - " \n" - " Тестовое описание метки\n" - " Test bookmark description\n" - " \n" - " \n" - " historic-castle\n" - " historic-memorial\n" - " \n" - " \n" - " My favorite place\n" - " Мое любимое место\n" - " \n" - " 15\n" - " \n" - " 0\n" - " \n" - " 0\n" - " 12345\n" - " \n" - " value1\n" - " value2\n" - " \n" - " \n" - " \n" - " \n" - " Test track\n" - " Test track description\n" - " \n" - " 1970-01-01T00:15:00Z\n" - " 45.9242,49.326859,1 45.2244,48.941288,2 45.1964,49.401948,3\n" - " \n" - " \n" - " Тестовый трек\n" - " Test track\n" - " \n" - " \n" - " Тестовое описание трека\n" - " Test track description\n" - " \n" - " 0\n" - " \n" - " \n" - " FF00FF00\n" - " 7\n" - " \n" - " \n" - " 0\n" - " \n" - " 12345\n" - " 54321\n" - " 98765\n" - " \n" - " \n" - " value1\n" - " value2\n" - " \n" - " \n" - " \n" - "\n" - ""; +R"( + + + + + + + + + + + + + + + + + + + Test category + Test description + 1 + + AAAA-BBBB-CCCC-DDDD + + Тестовая категория + Test category + + + Test annotation + Test annotation + + + Тестовое описание + Test description + + https://localhost/123.png + Maps.Me + 1970-01-01T00:16:40Z + 8.9 + 567 + Public + + mountains + ski + snowboard + + + 12345 + 54321 + + + en + ja + ru + + + value1 + value2 + + + + Тестовая коллекция + Test collection + + + Test collection annotation + Test collection annotation + + + Тестовое описание коллекции + Test collection description + + 1 + https://localhost/1234.png + Maps.Me + 1970-01-01T00:16:39Z + 5.9 + 333 + Public + + mountains + ski + + + 8 + 9 + + + en + ja + ru + + + value1 + value2 + + + + + Тестовая категория + Test category + + + Test category annotation + Test category annotation + + + Тестовое описание категории + Test category description + + 0 + https://localhost/134.png + Maps.Me + 1970-01-01T00:05:23Z + 3.3 + 222 + Public + + mountains + bike + + + 10 + 11 + + + en + ja + ru + + + value1 + value2 + + + + + Мое любимое место + Test bookmark description + 1970-01-01T00:13:20Z + #placemark-blue + 45.9242,49.326859 + + + Тестовая метка + Test bookmark + + + Тестовое описание метки + Test bookmark description + + + historic-castle + historic-memorial + + + My favorite place + Мое любимое место + + 15 + + 0 + + 0 + 12345 + + value1 + value2 + 1, 4 + 5 + + + + + Test track + Test track description + + 1970-01-01T00:15:00Z + 45.9242,49.326859,1 45.2244,48.941288,2 45.1964,49.401948,3 + + + Тестовый трек + Test track + + + Тестовое описание трека + Test track description + + 0 + + + FF00FF00 + 7 + + + 0 + + 12345 + 54321 + 98765 + + + value1 + value2 + + + + +)"; } // namespace // 1. Check text and binary deserialization from the prepared sources in memory. diff --git a/kml/kml_tests/tests_data.hpp b/kml/kml_tests/tests_data.hpp index c2ba606f2f..3cb8761e8d 100644 --- a/kml/kml_tests/tests_data.hpp +++ b/kml/kml_tests/tests_data.hpp @@ -1364,46 +1364,47 @@ std::vector const kBinKmlV4 = { }; std::vector const kBinKml = { - 0x08, 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, - 0x01, 0x00, 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x8D, 0xB7, 0xF5, 0x71, 0xFC, 0x8C, 0xFC, 0xC0, 0x02, 0x00, 0x01, 0x05, - 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xC2, 0xFB, 0xF9, - 0x01, 0xE3, 0xB9, 0xBB, 0x8E, 0x01, 0xC3, 0xC5, 0xD2, 0xBB, 0x02, 0x00, 0x01, 0x05, 0x01, 0x00, - 0x05, 0x01, 0x00, 0x06, 0x01, 0x00, 0x07, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0xBC, 0xED, 0xA7, - 0x03, 0x97, 0xB0, 0x9A, 0xA7, 0x02, 0xA4, 0xD6, 0xAE, 0xDB, 0x02, 0x00, 0x01, 0x05, 0x01, 0x00, - 0x08, 0x00, 0x01, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xCD, 0x97, 0xA7, 0x02, - 0xFD, 0xC1, 0xAC, 0xDB, 0x02, 0x00, 0x01, 0x05, 0x01, 0x00, 0x0A, 0x01, 0x00, 0x0B, 0x01, 0x00, - 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x0E, 0x08, 0x08, 0x1B, 0x1A, 0x1B, 0x41, 0x41, 0x0C, 0x11, 0x0C, 0x37, 0x3E, 0x00, 0x01, 0x00, - 0x01, 0x06, 0x01, 0x03, 0x09, 0x03, 0x05, 0x05, 0x07, 0x07, 0x07, 0x41, 0x09, 0x06, 0x0A, 0x0B, - 0x08, 0x8D, 0x01, 0x0D, 0x07, 0x10, 0x0F, 0x08, 0x71, 0x11, 0x05, 0x02, 0x13, 0x06, 0x04, 0x15, - 0x06, 0x0B, 0x17, 0x07, 0x5E, 0x19, 0x05, 0x0D, 0x1B, 0x07, 0xBA, 0x01, 0x1D, 0x08, 0x1C, 0x1F, - 0x06, 0x75, 0x21, 0x06, 0x06, 0x23, 0x06, 0x09, 0x27, 0x08, 0x4E, 0x29, 0x06, 0x12, 0x2B, 0x07, - 0x91, 0x01, 0x2D, 0x08, 0x14, 0x2F, 0x07, 0x73, 0x33, 0x06, 0x05, 0x35, 0x07, 0x0C, 0x37, 0x08, - 0x63, 0x3B, 0x07, 0xC0, 0x01, 0x3D, 0x08, 0x31, 0x3F, 0x07, 0x77, 0x43, 0x07, 0x08, 0x47, 0x08, - 0x48, 0x4B, 0x08, 0x90, 0x01, 0x4D, 0x07, 0x13, 0x4F, 0x07, 0x72, 0x57, 0x07, 0x62, 0x5B, 0x07, - 0xBD, 0x01, 0x5D, 0x07, 0x29, 0x67, 0x07, 0x57, 0x6B, 0x08, 0xA1, 0x01, 0x6D, 0x08, 0x18, 0x6F, - 0x08, 0x76, 0x75, 0x07, 0x0F, 0x77, 0x07, 0x68, 0x7B, 0x07, 0xD1, 0x01, 0x7D, 0x08, 0x3F, 0x7F, - 0x07, 0x79, 0x83, 0x01, 0x08, 0xB8, 0x01, 0x8B, 0x01, 0x08, 0x8F, 0x01, 0x8F, 0x01, 0x08, 0x74, - 0x9D, 0x01, 0x08, 0x25, 0xA7, 0x01, 0x08, 0x59, 0xAD, 0x01, 0x08, 0x17, 0xB7, 0x01, 0x08, 0x6D, - 0xBD, 0x01, 0x08, 0x3E, 0xC7, 0x01, 0x08, 0x4B, 0xCB, 0x01, 0x08, 0x96, 0x01, 0xEB, 0x01, 0x08, - 0xB7, 0x01, 0xED, 0x01, 0x08, 0x19, 0xEF, 0x01, 0x08, 0x8A, 0x01, 0xFD, 0x01, 0x08, 0x40, 0x83, - 0x02, 0x09, 0x0E, 0xA0, 0x02, 0xAF, 0x13, 0xE7, 0xEE, 0xAD, 0x1B, 0x51, 0x0F, 0x7D, 0x02, 0x8B, - 0xBA, 0xDC, 0x17, 0x6C, 0x0C, 0xE8, 0x3F, 0xF4, 0x7A, 0x70, 0x54, 0x0E, 0x25, 0xE5, 0x6D, 0xFE, - 0x26, 0xE1, 0xCF, 0xD5, 0xB1, 0x7A, 0xD1, 0x32, 0x1C, 0x8A, 0x5F, 0x54, 0xDA, 0xC4, 0x56, 0x9F, - 0xFC, 0x54, 0x5C, 0x8A, 0x49, 0x94, 0x65, 0x55, 0x23, 0x49, 0x43, 0x2F, 0xE7, 0x51, 0xAE, 0x19, - 0xFD, 0x9B, 0xCC, 0x95, 0xE7, 0x2C, 0xCB, 0xDF, 0xCF, 0x74, 0x1A, 0x01, 0x0C, 0x4D, 0x54, 0x52, - 0x77, 0xB5, 0x8B, 0x51, 0xB3, 0x3C, 0x22, 0x31, 0x30, 0xD4, 0x5E, 0x8D, 0x41, 0x3D, 0x11, 0x88, - 0x0D, 0xF3, 0x64, 0x9E, 0xFF, 0xD7, 0x70, 0x0F, 0x00, 0x80, 0x3D, 0x00, 0x00, 0x00, 0x80, 0x0E, - 0xB0, 0x45, 0xA7, 0x9D, 0x65, 0x6B, 0x78, 0xC7, 0xC6, 0xBA, 0x2D, 0x46, 0x83, 0x76, 0x08, 0xAC, - 0x14, 0x4B, 0x56, 0xA2, 0x09, 0x01, 0x00, 0x0D, + 0x08, 0x00, 0x00, 0x1E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0xB7, 0xF5, 0x71, 0xFC, 0x8C, 0xFC, 0xC0, 0x02, 0x00, 0x01, + 0x05, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xC2, 0xFB, + 0xF9, 0x01, 0xE3, 0xB9, 0xBB, 0x8E, 0x01, 0xC3, 0xC5, 0xD2, 0xBB, 0x02, 0x00, 0x01, 0x05, 0x01, + 0x00, 0x05, 0x01, 0x00, 0x06, 0x01, 0x00, 0x07, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0xBC, 0xED, + 0xA7, 0x03, 0x97, 0xB0, 0x9A, 0xA7, 0x02, 0xA4, 0xD6, 0xAE, 0xDB, 0x02, 0x00, 0x01, 0x05, 0x01, + 0x00, 0x08, 0x00, 0x01, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xCD, 0x97, 0xA7, + 0x02, 0xFD, 0xC1, 0xAC, 0xDB, 0x02, 0x00, 0x01, 0x05, 0x01, 0x00, 0x0A, 0x01, 0x00, 0x0B, 0x01, + 0x00, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x0E, 0x08, 0x08, 0x1B, 0x1A, 0x1B, 0x41, 0x41, 0x0C, 0x11, 0x0C, 0x37, 0x3E, 0x00, + 0x01, 0x00, 0x01, 0x06, 0x01, 0x03, 0x09, 0x03, 0x05, 0x05, 0x07, 0x07, 0x07, 0x41, 0x09, 0x06, + 0x0A, 0x0B, 0x08, 0x8D, 0x01, 0x0D, 0x07, 0x10, 0x0F, 0x08, 0x71, 0x11, 0x05, 0x02, 0x13, 0x06, + 0x04, 0x15, 0x06, 0x0B, 0x17, 0x07, 0x5E, 0x19, 0x05, 0x0D, 0x1B, 0x07, 0xBA, 0x01, 0x1D, 0x08, + 0x1C, 0x1F, 0x06, 0x75, 0x21, 0x06, 0x06, 0x23, 0x06, 0x09, 0x27, 0x08, 0x4E, 0x29, 0x06, 0x12, + 0x2B, 0x07, 0x91, 0x01, 0x2D, 0x08, 0x14, 0x2F, 0x07, 0x73, 0x33, 0x06, 0x05, 0x35, 0x07, 0x0C, + 0x37, 0x08, 0x63, 0x3B, 0x07, 0xC0, 0x01, 0x3D, 0x08, 0x31, 0x3F, 0x07, 0x77, 0x43, 0x07, 0x08, + 0x47, 0x08, 0x48, 0x4B, 0x08, 0x90, 0x01, 0x4D, 0x07, 0x13, 0x4F, 0x07, 0x72, 0x57, 0x07, 0x62, + 0x5B, 0x07, 0xBD, 0x01, 0x5D, 0x07, 0x29, 0x67, 0x07, 0x57, 0x6B, 0x08, 0xA1, 0x01, 0x6D, 0x08, + 0x18, 0x6F, 0x08, 0x76, 0x75, 0x07, 0x0F, 0x77, 0x07, 0x68, 0x7B, 0x07, 0xD1, 0x01, 0x7D, 0x08, + 0x3F, 0x7F, 0x07, 0x79, 0x83, 0x01, 0x08, 0xB8, 0x01, 0x8B, 0x01, 0x08, 0x8F, 0x01, 0x8F, 0x01, + 0x08, 0x74, 0x9D, 0x01, 0x08, 0x25, 0xA7, 0x01, 0x08, 0x59, 0xAD, 0x01, 0x08, 0x17, 0xB7, 0x01, + 0x08, 0x6D, 0xBD, 0x01, 0x08, 0x3E, 0xC7, 0x01, 0x08, 0x4B, 0xCB, 0x01, 0x08, 0x96, 0x01, 0xEB, + 0x01, 0x08, 0xB7, 0x01, 0xED, 0x01, 0x08, 0x19, 0xEF, 0x01, 0x08, 0x8A, 0x01, 0xFD, 0x01, 0x08, + 0x40, 0x83, 0x02, 0x09, 0x0E, 0xA0, 0x02, 0xAF, 0x13, 0xE7, 0xEE, 0xAD, 0x1B, 0x51, 0x0F, 0x7D, + 0x02, 0x8B, 0xBA, 0xDC, 0x17, 0x6C, 0x0C, 0xE8, 0x3F, 0xF4, 0x7A, 0x70, 0x54, 0x0E, 0x25, 0xE5, + 0x6D, 0xFE, 0x26, 0xE1, 0xCF, 0xD5, 0xB1, 0x7A, 0xD1, 0x32, 0x1C, 0x8A, 0x5F, 0x54, 0xDA, 0xC4, + 0x56, 0x9F, 0xFC, 0x54, 0x5C, 0x8A, 0x49, 0x94, 0x65, 0x55, 0x23, 0x49, 0x43, 0x2F, 0xE7, 0x51, + 0xAE, 0x19, 0xFD, 0x9B, 0xCC, 0x95, 0xE7, 0x2C, 0xCB, 0xDF, 0xCF, 0x74, 0x1A, 0x01, 0x0C, 0x4D, + 0x54, 0x52, 0x77, 0xB5, 0x8B, 0x51, 0xB3, 0x3C, 0x22, 0x31, 0x30, 0xD4, 0x5E, 0x8D, 0x41, 0x3D, + 0x11, 0x88, 0x0D, 0xF3, 0x64, 0x9E, 0xFF, 0xD7, 0x70, 0x0F, 0x00, 0x80, 0x3D, 0x00, 0x00, 0x00, + 0x80, 0x0E, 0xB0, 0x45, 0xA7, 0x9D, 0x65, 0x6B, 0x78, 0xC7, 0xC6, 0xBA, 0x2D, 0x46, 0x83, 0x76, + 0x08, 0xAC, 0x14, 0x4B, 0x56, 0xA2, 0x09, 0x01, 0x00, 0x0D, }; std::vector const kBinKmlV6 = { diff --git a/kml/serdes.cpp b/kml/serdes.cpp index e5787c39e2..92361da636 100644 --- a/kml/serdes.cpp +++ b/kml/serdes.cpp @@ -28,6 +28,7 @@ std::string const kStyleMap = "StyleMap"; std::string const kStyleUrl = "styleUrl"; std::string const kPair = "Pair"; std::string const kExtendedData = "ExtendedData"; +std::string const kCompilation = "mwm:compilation"; std::string const kKmlHeader = "\n" @@ -44,6 +45,8 @@ std::string const kExtendedDataHeader = std::string const kExtendedDataFooter = "\n"; +std::string const kCompilationFooter = "\n"; + auto const kDefaultLang = StringUtf8Multilang::kDefaultCode; auto const kDefaultTrackWidth = 5.0; @@ -187,18 +190,19 @@ void SaveStringWithCDATA(KmlWriter::WriterWrapper & writer, std::string const & writer << s; } -void SaveStyle(KmlWriter::WriterWrapper & writer, std::string const & style) +void SaveStyle(KmlWriter::WriterWrapper & writer, std::string const & style, bool isCompilationData) { if (style.empty()) return; - writer << kIndent2 << "\n"; + writer << (isCompilationData ? kIndent4 : kIndent2) << "\n"; } void SaveColorToABGR(KmlWriter::WriterWrapper & writer, uint32_t rgba) @@ -266,53 +270,75 @@ void SaveStringsMap(KmlWriter::WriterWrapper & writer, writer << offsetStr << "\n"; } +void SaveCategoryData(KmlWriter::WriterWrapper & writer, CategoryData const & categoryData, + std::string const & extendedServerId, + std::vector const * compilationData); + void SaveCategoryExtendedData(KmlWriter::WriterWrapper & writer, CategoryData const & categoryData, - std::string const & extendedServerId) + std::string const & extendedServerId, + std::vector const * compilationData) { - writer << kIndent2 << kExtendedDataHeader; + if (compilationData) + { + writer << kIndent2 << kExtendedDataHeader; + } + else + { + std::string compilationAttributes; + if (categoryData.m_compilationId != kInvalidCompilationId) + compilationAttributes += " id=\"" + strings::to_string(categoryData.m_compilationId) + "\""; + compilationAttributes += " type=\"" + DebugPrint(categoryData.m_type) + "\""; + writer << kIndent4 << "<" << kCompilation << compilationAttributes << ">\n"; + } - if (!extendedServerId.empty()) - writer << kIndent4 << "" << extendedServerId << "\n"; + auto const & indent = compilationData ? kIndent4 : kIndent6; - SaveLocalizableString(writer, categoryData.m_name, "name", kIndent4); - SaveLocalizableString(writer, categoryData.m_annotation, "annotation", kIndent4); - SaveLocalizableString(writer, categoryData.m_description, "description", kIndent4); + if (!extendedServerId.empty() && compilationData) + writer << indent << "" << extendedServerId << "\n"; + + SaveLocalizableString(writer, categoryData.m_name, "name", indent); + SaveLocalizableString(writer, categoryData.m_annotation, "annotation", indent); + SaveLocalizableString(writer, categoryData.m_description, "description", indent); + + if (!compilationData) + writer << indent << "" << (categoryData.m_visible ? "1" : "0") + << "\n"; if (!categoryData.m_imageUrl.empty()) - writer << kIndent4 << "" << categoryData.m_imageUrl << "\n"; + writer << indent << "" << categoryData.m_imageUrl << "\n"; if (!categoryData.m_authorId.empty() || !categoryData.m_authorName.empty()) { - writer << kIndent4 << ""; + writer << indent << ""; SaveStringWithCDATA(writer, categoryData.m_authorName); writer << "\n"; } if (categoryData.m_lastModified != Timestamp()) { - writer << kIndent4 << "" << TimestampToString(categoryData.m_lastModified) + writer << indent << "" << TimestampToString(categoryData.m_lastModified) << "\n"; } double constexpr kEps = 1e-5; if (fabs(categoryData.m_rating) > kEps) { - writer << kIndent4 << "" << strings::to_string(categoryData.m_rating) + writer << indent << "" << strings::to_string(categoryData.m_rating) << "\n"; } if (categoryData.m_reviewsNumber > 0) { - writer << kIndent4 << "" - << strings::to_string(categoryData.m_reviewsNumber) << "\n"; + writer << indent << "" << strings::to_string(categoryData.m_reviewsNumber) + << "\n"; } - writer << kIndent4 << "" << DebugPrint(categoryData.m_accessRules) + writer << indent << "" << DebugPrint(categoryData.m_accessRules) << "\n"; - SaveStringsArray(writer, categoryData.m_tags, "tags", kIndent4); + SaveStringsArray(writer, categoryData.m_tags, "tags", indent); - SaveStringsArray(writer, categoryData.m_toponyms, "toponyms", kIndent4); + SaveStringsArray(writer, categoryData.m_toponyms, "toponyms", indent); std::vector languageCodes; languageCodes.reserve(categoryData.m_languageCodes.size()); @@ -322,33 +348,54 @@ void SaveCategoryExtendedData(KmlWriter::WriterWrapper & writer, CategoryData co if (!str.empty()) languageCodes.push_back(std::move(str)); } - SaveStringsArray(writer, languageCodes, "languageCodes", kIndent4); + SaveStringsArray(writer, languageCodes, "languageCodes", indent); - SaveStringsMap(writer, categoryData.m_properties, "properties", kIndent4); + SaveStringsMap(writer, categoryData.m_properties, "properties", indent); - writer << kIndent2 << kExtendedDataFooter; + if (compilationData) + { + for (auto const & compilationDatum : *compilationData) + SaveCategoryData(writer, compilationDatum, {} /* extendedServerId */, + nullptr /* compilationData */); + } + + if (compilationData) + writer << kIndent2 << kExtendedDataFooter; + else + writer << kIndent4 << kCompilationFooter; } void SaveCategoryData(KmlWriter::WriterWrapper & writer, CategoryData const & categoryData, - std::string const & extendedServerId) + std::string const & extendedServerId, + std::vector const * compilationData) { - for (uint8_t i = 0; i < base::Underlying(PredefinedColor::Count); ++i) - SaveStyle(writer, GetStyleForPredefinedColor(static_cast(i))); - - // Use CDATA if we have special symbols in the name. - writer << kIndent2 << ""; - SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_name, kDefaultLang)); - writer << "\n"; - if (!categoryData.m_description.empty()) + if (compilationData) { - writer << kIndent2 << ""; - SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_description, kDefaultLang)); - writer << "\n"; + for (uint8_t i = 0; i < base::Underlying(PredefinedColor::Count); ++i) + SaveStyle(writer, GetStyleForPredefinedColor(static_cast(i)), + !compilationData); } - writer << kIndent2 << "" << (categoryData.m_visible ? "1" : "0") <<"\n"; + auto const & indent = compilationData ? kIndent2 : kIndent4; - SaveCategoryExtendedData(writer, categoryData, extendedServerId); + if (compilationData) + { + // Use CDATA if we have special symbols in the name. + writer << indent << ""; + SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_name, kDefaultLang)); + writer << "\n"; + + if (!categoryData.m_description.empty()) + { + writer << indent << ""; + SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_description, kDefaultLang)); + writer << "\n"; + } + + writer << indent << "" << (categoryData.m_visible ? "1" : "0") << "\n"; + } + + SaveCategoryExtendedData(writer, categoryData, extendedServerId, compilationData); } void SaveBookmarkExtendedData(KmlWriter::WriterWrapper & writer, BookmarkData const & bookmarkData) @@ -572,7 +619,8 @@ void KmlWriter::Write(FileData const & fileData) m_writer << kKmlHeader; // Save category. - SaveCategoryData(m_writer, fileData.m_categoryData, fileData.m_serverId); + SaveCategoryData(m_writer, fileData.m_categoryData, fileData.m_serverId, + &fileData.m_compilationData); // Save bookmarks. for (auto const & bookmarkData : fileData.m_bookmarksData) @@ -587,6 +635,7 @@ void KmlWriter::Write(FileData const & fileData) KmlParser::KmlParser(FileData & data) : m_data(data) + , m_categoryData(&m_data.m_categoryData) , m_attrCode(StringUtf8Multilang::kUnsupportedLanguageCode) { ResetPoint(); @@ -716,9 +765,14 @@ double KmlParser::GetTrackWidthForStyle(std::string const & styleUrl) const return kDefaultTrackWidth; } -bool KmlParser::Push(std::string const & name) +bool KmlParser::Push(std::string const & tag) { - m_tags.push_back(name); + m_tags.push_back(tag); + if (tag == kCompilation) + { + m_categoryData = &m_compilationData; + m_compilationData.m_accessRules = m_data.m_categoryData.m_accessRules; + } return true; } @@ -728,16 +782,44 @@ void KmlParser::AddAttr(std::string const & attr, std::string const & value) strings::AsciiToLower(attrInLowerCase); if (IsValidAttribute(kStyle, value, attrInLowerCase)) + { m_styleId = value; + } else if (IsValidAttribute(kStyleMap, value, attrInLowerCase)) + { m_mapStyleId = value; + } + else if (IsValidAttribute(kCompilation, value, attrInLowerCase)) + { + if (!strings::to_uint64(value, m_categoryData->m_compilationId)) + m_categoryData->m_compilationId = 0; + } if (attrInLowerCase == "code") + { m_attrCode = StringUtf8Multilang::GetLangIndex(value); + } else if (attrInLowerCase == "id") + { m_attrId = value; + } else if (attrInLowerCase == "key") + { m_attrKey = value; + } + else if (attrInLowerCase == "type" && !value.empty() && GetTagFromEnd(0) == kCompilation) + { + std::string valueInLowerCase = value; + strings::AsciiToLower(valueInLowerCase); + if (valueInLowerCase == "category") + m_categoryData->m_type = CompilationType::Category; + else if (valueInLowerCase == "collection") + m_categoryData->m_type = CompilationType::Collection; + else if (valueInLowerCase == "day") + m_categoryData->m_type = CompilationType::Day; + else + m_categoryData->m_type = CompilationType::Category; + } } bool KmlParser::IsValidAttribute(std::string const & type, std::string const & value, @@ -832,6 +914,11 @@ void KmlParser::Pop(std::string const & tag) m_trackWidth = kDefaultTrackWidth; m_color = 0; } + else if (tag == kCompilation) + { + m_data.m_compilationData.push_back(std::move(m_compilationData)); + m_categoryData = &m_data.m_categoryData; + } m_tags.pop_back(); } @@ -847,94 +934,104 @@ void KmlParser::CharData(std::string value) std::string const & prevTag = m_tags[count - 2]; std::string const ppTag = count > 2 ? m_tags[count - 3] : std::string(); std::string const pppTag = count > 3 ? m_tags[count - 4] : std::string(); + std::string const ppppTag = count > 4 ? m_tags[count - 5] : std::string(); if (prevTag == kDocument) { if (currTag == "name") - m_data.m_categoryData.m_name[kDefaultLang] = value; + m_categoryData->m_name[kDefaultLang] = value; else if (currTag == "description") - m_data.m_categoryData.m_description[kDefaultLang] = value; + m_categoryData->m_description[kDefaultLang] = value; else if (currTag == "visibility") - m_data.m_categoryData.m_visible = value != "0"; + m_categoryData->m_visible = value != "0"; } - else if (prevTag == kExtendedData && ppTag == kDocument) + else if ((prevTag == kExtendedData && ppTag == kDocument) || + (prevTag == kCompilation && ppTag == kExtendedData && pppTag == kDocument)) { if (currTag == "mwm:author") { - m_data.m_categoryData.m_authorName = value; - m_data.m_categoryData.m_authorId = m_attrId; + m_categoryData->m_authorName = value; + m_categoryData->m_authorId = m_attrId; m_attrId.clear(); } else if (currTag == "mwm:lastModified") { auto const ts = base::StringToTimestamp(value); if (ts != base::INVALID_TIME_STAMP) - m_data.m_categoryData.m_lastModified = std::chrono::system_clock::from_time_t(ts); + m_categoryData->m_lastModified = std::chrono::system_clock::from_time_t(ts); } else if (currTag == "mwm:accessRules") { // 'Private' is here for back-compatibility. if (value == "Private" || value == "Local") - m_data.m_categoryData.m_accessRules = AccessRules::Local; + m_categoryData->m_accessRules = AccessRules::Local; else if (value == "DirectLink") - m_data.m_categoryData.m_accessRules = AccessRules::DirectLink; + m_categoryData->m_accessRules = AccessRules::DirectLink; else if (value == "P2P") - m_data.m_categoryData.m_accessRules = AccessRules::P2P; + m_categoryData->m_accessRules = AccessRules::P2P; else if (value == "Paid") - m_data.m_categoryData.m_accessRules = AccessRules::Paid; + m_categoryData->m_accessRules = AccessRules::Paid; else if (value == "Public") - m_data.m_categoryData.m_accessRules = AccessRules::Public; + m_categoryData->m_accessRules = AccessRules::Public; else if (value == "AuthorOnly") - m_data.m_categoryData.m_accessRules = AccessRules::AuthorOnly; + m_categoryData->m_accessRules = AccessRules::AuthorOnly; } else if (currTag == "mwm:imageUrl") { - m_data.m_categoryData.m_imageUrl = value; + m_categoryData->m_imageUrl = value; } else if (currTag == "mwm:rating") { - if (!strings::to_double(value, m_data.m_categoryData.m_rating)) - m_data.m_categoryData.m_rating = 0.0; + if (!strings::to_double(value, m_categoryData->m_rating)) + m_categoryData->m_rating = 0.0; } else if (currTag == "mwm:reviewsNumber") { - if (!strings::to_uint(value, m_data.m_categoryData.m_reviewsNumber)) - m_data.m_categoryData.m_reviewsNumber = 0; + if (!strings::to_uint(value, m_categoryData->m_reviewsNumber)) + m_categoryData->m_reviewsNumber = 0; } else if (currTag == "mwm:serverId") { m_data.m_serverId = value; } + else if (currTag == "mwm:visibility") + { + m_categoryData->m_visible = value != "0"; + } } - else if (pppTag == kDocument && ppTag == kExtendedData && currTag == "mwm:lang") + else if (((pppTag == kDocument && ppTag == kExtendedData) || + (ppppTag == kDocument && pppTag == kExtendedData && ppTag == kCompilation)) && + currTag == "mwm:lang") { if (prevTag == "mwm:name" && m_attrCode >= 0) - m_data.m_categoryData.m_name[m_attrCode] = value; + m_categoryData->m_name[m_attrCode] = value; else if (prevTag == "mwm:description" && m_attrCode >= 0) - m_data.m_categoryData.m_description[m_attrCode] = value; + m_categoryData->m_description[m_attrCode] = value; else if (prevTag == "mwm:annotation" && m_attrCode >= 0) - m_data.m_categoryData.m_annotation[m_attrCode] = value; + m_categoryData->m_annotation[m_attrCode] = value; m_attrCode = StringUtf8Multilang::kUnsupportedLanguageCode; } - else if (pppTag == kDocument && ppTag == kExtendedData && currTag == "mwm:value") + else if (((pppTag == kDocument && ppTag == kExtendedData) || + (ppppTag == kDocument && pppTag == kExtendedData && ppTag == kCompilation)) && + currTag == "mwm:value") { if (prevTag == "mwm:tags") { - m_data.m_categoryData.m_tags.push_back(value); + m_categoryData->m_tags.push_back(value); } else if (prevTag == "mwm:toponyms") { - m_data.m_categoryData.m_toponyms.push_back(value); + m_categoryData->m_toponyms.push_back(value); } else if (prevTag == "mwm:languageCodes") { auto const lang = StringUtf8Multilang::GetLangIndex(value); if (lang != StringUtf8Multilang::kUnsupportedLanguageCode) - m_data.m_categoryData.m_languageCodes.push_back(lang); + m_categoryData->m_languageCodes.push_back(lang); } else if (prevTag == "mwm:properties" && !m_attrKey.empty()) { - m_data.m_categoryData.m_properties[m_attrKey] = value; + m_categoryData->m_properties[m_attrKey] = value; m_attrKey.clear(); } } diff --git a/kml/serdes.hpp b/kml/serdes.hpp index ec13e8bd47..a2e4034910 100644 --- a/kml/serdes.hpp +++ b/kml/serdes.hpp @@ -95,6 +95,8 @@ private: double GetTrackWidthForStyle(std::string const & styleUrl) const; FileData & m_data; + CategoryData m_compilationData; + CategoryData * m_categoryData; std::vector m_tags; GeometryType m_geometryType; diff --git a/kml/serdes_binary.hpp b/kml/serdes_binary.hpp index 9c0976e326..1092a95407 100644 --- a/kml/serdes_binary.hpp +++ b/kml/serdes_binary.hpp @@ -21,22 +21,6 @@ namespace kml { 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. 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. - V8 = 8, // TODO(tomilov): - Latest = V8 -}; - class SerializerKml { public: @@ -86,6 +70,10 @@ public: header.m_tracksOffset = sink.Pos() - startPos; SerializeTracks(sink); + // Serialize tracks. + header.m_compilationsOffset = sink.Pos() - startPos; + SerializeCompilations(sink); + // Serialize strings. header.m_stringsOffset = sink.Pos() - startPos; SerializeStrings(sink); @@ -118,6 +106,13 @@ public: visitor(m_data.m_tracksData); } + template + void SerializeCompilations(Sink & sink) + { + CategorySerializerVisitor visitor(sink, kDoubleBits); + visitor(m_data.m_compilationData); + } + // Serializes texts in a compressed storage with block access. template void SerializeStrings(Sink & sink) @@ -144,11 +139,12 @@ public: { // Check version. NonOwningReaderSource source(reader); - auto const v = ReadPrimitiveFromSource(source); + m_header.m_version = ReadPrimitiveFromSource(source); - if (v != Version::V2 && v != Version::V3 && v != Version::V4 && - v != Version::V5 && v != Version::V6 && v != Version::V7 && - v != Version::V8) + if (m_header.m_version != Version::V2 && m_header.m_version != Version::V3 && + m_header.m_version != Version::V4 && m_header.m_version != Version::V5 && + m_header.m_version != Version::V6 && m_header.m_version != Version::V7 && + m_header.m_version != Version::V8) { MYTHROW(DeserializeException, ("Incorrect file version.")); } @@ -160,7 +156,7 @@ public: auto subReader = reader.CreateSubReader(source.Pos(), source.Size()); InitializeIfNeeded(*subReader); - switch (v) + switch (m_header.m_version) { case Version::Latest: { @@ -172,7 +168,7 @@ public: FileDataV7 dataV7; dataV7.m_deviceId = m_data.m_deviceId; dataV7.m_serverId = m_data.m_serverId; - DeserializeFileData(subReader, dataV7); + DeserializeFileDataBeforeV8(subReader, dataV7); m_data = dataV7.ConvertToLatestVersion(); break; @@ -185,7 +181,7 @@ public: FileDataV6 dataV6; dataV6.m_deviceId = m_data.m_deviceId; dataV6.m_serverId = m_data.m_serverId; - DeserializeFileData(subReader, dataV6); + DeserializeFileDataBeforeV8(subReader, dataV6); m_data = dataV6.ConvertToLatestVersion(); break; @@ -197,10 +193,10 @@ public: FileDataV3 dataV3; dataV3.m_deviceId = m_data.m_deviceId; dataV3.m_serverId = m_data.m_serverId; - DeserializeFileData(subReader, dataV3); + DeserializeFileDataBeforeV8(subReader, dataV3); // Migrate bookmarks (it's necessary ony for v.2). - if (v == Version::V2) + if (m_header.m_version == Version::V2) MigrateBookmarksV2(dataV3); m_data = dataV3.ConvertToLatestVersion(); @@ -250,7 +246,13 @@ private: template std::unique_ptr CreateTrackSubReader(ReaderType const & reader) { - return CreateSubReader(reader, m_header.m_tracksOffset, m_header.m_stringsOffset); + return CreateSubReader(reader, m_header.m_tracksOffset, m_header.m_compilationsOffset); + } + + template + std::unique_ptr CreateCompilationsSubReader(ReaderType const & reader) + { + return CreateSubReader(reader, m_header.m_compilationsOffset, m_header.m_stringsOffset); } template @@ -282,6 +284,20 @@ private: 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); + if (m_header.HasCompilationsSection()) + DeserializeCompilations(subReader, data); + DeserializeStrings(subReader, data); + } + + template + void DeserializeFileDataBeforeV8(std::unique_ptr & subReader, FileDataType & data) { // Keep in mind - deserialization/serialization works in two stages: // - serialization/deserialization non-string members of structures; @@ -325,6 +341,15 @@ private: visitor(data.m_tracksData); } + template + void DeserializeCompilations(std::unique_ptr & subReader, FileDataType & data) + { + auto compilationsSubReader = CreateCompilationsSubReader(*subReader); + NonOwningReaderSource src(*compilationsSubReader); + CategoryDeserializerVisitor visitor(src, m_doubleBits); + visitor(data.m_compilationData); + } + template void DeserializeStrings(std::unique_ptr & subReader, FileDataType & data) { diff --git a/kml/types.hpp b/kml/types.hpp index cfe3f5940e..f7fe807d93 100644 --- a/kml/types.hpp +++ b/kml/types.hpp @@ -229,7 +229,6 @@ struct BookmarkData visitor(m_visible, "visible"), visitor(m_nearestToponym, "nearestToponym"), visitor(m_properties, "properties"), - //visitor(m_compilations, "compilations"), // TODO(tomilov): enable ASAP VISITOR_COLLECTABLE) DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description, m_customName, @@ -248,8 +247,7 @@ struct BookmarkData m_boundTracks == data.m_boundTracks && m_visible == data.m_visible && m_nearestToponym == data.m_nearestToponym && - m_properties == data.m_properties && - m_compilations == data.m_compilations; + m_properties == data.m_properties; } bool operator!=(BookmarkData const & data) const { return !operator==(data); } @@ -283,8 +281,6 @@ struct BookmarkData std::string m_nearestToponym; // Key-value properties. Properties m_properties; - // List of compilationIds. - std::vector m_compilations; }; // Note: any changes in binary format of this structure @@ -365,8 +361,8 @@ struct TrackData struct CategoryData { DECLARE_VISITOR_AND_DEBUG_PRINT(CategoryData, visitor(m_id, "id"), - //visitor(m_compilationId, "compilationId"), // TODO(tomilov): enable ASAP - //visitor(m_type, "type"), // TODO(tomilov): enable ASAP + visitor(m_compilationId, "compilationId"), + visitor(m_type, "type"), visitor(m_name, "name"), visitor(m_imageUrl, "imageUrl"), visitor(m_annotation, "annotation"), @@ -448,12 +444,14 @@ struct FileData DECLARE_VISITOR_AND_DEBUG_PRINT(FileData, visitor(m_serverId, "serverId"), visitor(m_categoryData, "category"), visitor(m_bookmarksData, "bookmarks"), - visitor(m_tracksData, "tracks")) + visitor(m_tracksData, "tracks"), + visitor(m_compilationData, "compilations")) bool operator==(FileData const & data) const { return m_serverId == data.m_serverId && m_categoryData == data.m_categoryData && - m_bookmarksData == data.m_bookmarksData && m_tracksData == data.m_tracksData; + m_bookmarksData == data.m_bookmarksData && m_tracksData == data.m_tracksData && + m_compilationData == data.m_compilationData; } bool operator!=(FileData const & data) const { return !operator==(data); } diff --git a/kml/visitors.hpp b/kml/visitors.hpp index 7d058c39d7..e56a5e6af4 100644 --- a/kml/visitors.hpp +++ b/kml/visitors.hpp @@ -281,7 +281,12 @@ public: (*this)(static_cast(rules)); } - void operator()(Timestamp const & t, char const * name = nullptr) + void operator()(CompilationType type, char const * /* name */ = nullptr) + { + (*this)(static_cast(type)); + } + + void operator()(Timestamp const & t, char const * /* name */ = nullptr) { WriteVarUint(m_sink, ToSecondsSinceEpoch(t)); } @@ -297,6 +302,11 @@ public: WritePointD(m_sink, pt, m_doubleBits); } + void operator()(CategoryData const & compilationData, char const * /* name */ = nullptr) + { + compilationData.Visit(*this); + } + template void operator()(std::vector const & vs, char const * /* name */ = nullptr) { @@ -471,6 +481,11 @@ public: rules = static_cast(ReadPrimitiveFromSource(m_source)); } + void operator()(CompilationType & type, char const * /* name */ = nullptr) + { + type = static_cast(ReadPrimitiveFromSource(m_source)); + } + void operator()(Timestamp & t, char const * /* name */ = nullptr) { auto const v = ReadVarUint(m_source); @@ -488,6 +503,11 @@ public: pt = ReadPointD(m_source, m_doubleBits); } + void operator()(CategoryData & compilationData, char const * /* name */ = nullptr) + { + compilationData.Visit(*this); + } + template void operator()(std::vector & vs, char const * /* name */ = nullptr) {