diff --git a/kml/CMakeLists.txt b/kml/CMakeLists.txt index 9bdd70f049..eb66185d7f 100644 --- a/kml/CMakeLists.txt +++ b/kml/CMakeLists.txt @@ -10,6 +10,7 @@ set( type_utils.cpp type_utils.hpp types.hpp + types_v3.hpp visitors.hpp ) diff --git a/kml/kml_tests/serdes_tests.cpp b/kml/kml_tests/serdes_tests.cpp index ce41a21927..31befccc82 100644 --- a/kml/kml_tests/serdes_tests.cpp +++ b/kml/kml_tests/serdes_tests.cpp @@ -124,7 +124,7 @@ char const * kTextKml = "" ""; -std::vector const kBinKml = { +std::vector const kBinKmlV3 = { 0x03, 0x00, 0x00, 0x1E, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, @@ -166,6 +166,49 @@ std::vector const kBinKml = { 0x09, 0x01, 0x00, 0x0D, }; +std::vector const kBinKml = { + 0x04, 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, 0x01, 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, +}; + // This function can be used to generate textual representation of vector like you see above. std::string FormatBytesFromBuffer(std::vector const & buffer) { @@ -204,7 +247,7 @@ kml::FileData GenerateKmlFileData() result.m_categoryData.m_lastModified = std::chrono::system_clock::from_time_t(1000); result.m_categoryData.m_accessRules = kml::AccessRules::Public; result.m_categoryData.m_tags = {"mountains", "ski", "snowboard"}; - result.m_categoryData.m_cities = {m2::PointD(35.0, 56.0), m2::PointD(20.0, 68.0)}; + result.m_categoryData.m_toponyms = {"12345", "54321"}; result.m_categoryData.m_languageCodes = {1, 2, 8}; result.m_categoryData.m_properties = {{"property1", "value1"}, {"property2", "value2"}}; @@ -222,6 +265,9 @@ kml::FileData GenerateKmlFileData() bookmarkData.m_timestamp = std::chrono::system_clock::from_time_t(800); bookmarkData.m_point = m2::PointD(45.9242, 56.8679); bookmarkData.m_boundTracks = {0}; + bookmarkData.m_visible = false; + bookmarkData.m_nearestToponym = "12345"; + bookmarkData.m_properties = {{"bm_property1", "value1"}, {"bm_property2", "value2"}}; result.m_bookmarksData.emplace_back(std::move(bookmarkData)); kml::TrackData trackData; @@ -235,6 +281,9 @@ kml::FileData GenerateKmlFileData() 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_visible = false; + trackData.m_nearestToponyms = {"12345", "54321", "98765"}; + trackData.m_properties = {{"tr_property1", "value1"}, {"tr_property2", "value2"}}; result.m_tracksData.emplace_back(std::move(trackData)); return result; @@ -328,10 +377,10 @@ char const * kGeneratedKml = " ski\n" " snowboard\n" " \n" - " \n" - " 35,48.757959\n" - " 20,56.056771\n" - " \n" + " \n" + " 12345\n" + " 54321\n" + " \n" " \n" " en\n" " ja\n" @@ -369,6 +418,12 @@ char const * kGeneratedKml = " \n" " 0\n" " \n" + " 0\n" + " 12345\n" + " \n" + " value1\n" + " value2\n" + " \n" " \n" " \n" " \n" @@ -396,6 +451,16 @@ char const * kGeneratedKml = " 7\n" " \n" " \n" + " 0\n" + " \n" + " 12345\n" + " 54321\n" + " 98765\n" + " \n" + " \n" + " value1\n" + " value2\n" + " \n" " \n" " \n" "\n" @@ -664,3 +729,33 @@ UNIT_TEST(Kml_Serialization_Text_File) } TEST_EQUAL(dataFromMemory, data, ()); } + +// 8. Check binary deserialization of v.3 format. +UNIT_TEST(Kml_Deserialization_Bin_V3) +{ + kml::FileData dataFromText; + try + { + kml::DeserializerKml des(dataFromText); + MemReader reader(kTextKml, strlen(kTextKml)); + des.Deserialize(reader); + } + catch (kml::DeserializerKml::DeserializeException const & exc) + { + TEST(false, ("Exception raised", exc.what())); + } + + kml::FileData dataFromBin; + try + { + MemReader reader(kBinKmlV3.data(), kBinKmlV3.size()); + kml::binary::DeserializerKml des(dataFromBin); + des.Deserialize(reader); + } + catch (kml::binary::DeserializerKml::DeserializeException const & exc) + { + TEST(false, ("Exception raised", exc.what())); + } + + TEST_EQUAL(dataFromText, dataFromBin, ()); +} diff --git a/kml/pykmlib/bindings.cpp b/kml/pykmlib/bindings.cpp index debb199112..20845c6a5a 100644 --- a/kml/pykmlib/bindings.cpp +++ b/kml/pykmlib/bindings.cpp @@ -382,7 +382,10 @@ std::string BookmarkDataToString(BookmarkData const & bm) << "viewport_scale:" << static_cast(bm.m_viewportScale) << ", " << "timestamp:" << DebugPrint(bm.m_timestamp) << ", " << "point:" << LatLonToString(latLon) << ", " - << "bound_tracks:" << VectorAdapter::ToString(bm.m_boundTracks) + << "bound_tracks:" << VectorAdapter::ToString(bm.m_boundTracks) << ", " + << "visible:" << (bm.m_visible ? "True" : "False") << ", " + << "nearest_toponym:'" << bm.m_nearestToponym << "', " + << "properties:" << PropertiesAdapter::ToString(bm.m_properties) << "]"; return out.str(); } @@ -406,7 +409,10 @@ std::string TrackDataToString(TrackData const & t) << "description:" << LocalizableStringAdapter::ToString(t.m_description) << ", " << "timestamp:" << DebugPrint(t.m_timestamp) << ", " << "layers:" << VectorAdapter::ToString(t.m_layers) << ", " - << "points:" << VectorAdapter::ToString(t.m_points) + << "points:" << VectorAdapter::ToString(t.m_points) << ", " + << "visible:" << (t.m_visible ? "True" : "False") << ", " + << "nearest_toponyms:" << VectorAdapter::ToString(t.m_nearestToponyms) << ", " + << "properties:" << PropertiesAdapter::ToString(t.m_properties) << "]"; return out.str(); } @@ -441,7 +447,7 @@ std::string CategoryDataToString(CategoryData const & c) << "reviews_number:" << c.m_reviewsNumber << ", " << "access_rules:" << AccessRulesToString(c.m_accessRules) << ", " << "tags:" << VectorAdapter::ToString(c.m_tags) << ", " - << "cities:" << VectorAdapter::ToString(c.m_cities) << ", " + << "toponyms:" << VectorAdapter::ToString(c.m_toponyms) << ", " << "languages:" << LanguagesListToString(c.m_languageCodes) << ", " << "properties:" << PropertiesAdapter::ToString(c.m_properties) << "]"; @@ -738,6 +744,16 @@ BOOST_PYTHON_MODULE(pykmlib) class_("Timestamp"); + class_("Properties") + .def("__len__", &Properties::size) + .def("clear", &Properties::clear) + .def("__getitem__", &PropertiesAdapter::Get, return_value_policy()) + .def("__setitem__", &PropertiesAdapter::Set, with_custodian_and_ward<1,2>()) + .def("__delitem__", &PropertiesAdapter::Delete) + .def("get_dict", &PropertiesAdapter::GetDict) + .def("set_dict", &PropertiesAdapter::SetDict) + .def("__str__", &PropertiesAdapter::ToString); + class_("BookmarkData") .def_readwrite("name", &BookmarkData::m_name) .def_readwrite("description", &BookmarkData::m_description) @@ -749,6 +765,9 @@ BOOST_PYTHON_MODULE(pykmlib) .def_readwrite("timestamp", &BookmarkData::m_timestamp) .def_readwrite("point", &BookmarkData::m_point) .def_readwrite("bound_tracks", &BookmarkData::m_boundTracks) + .def_readwrite("visible", &BookmarkData::m_visible) + .def_readwrite("nearest_toponym", &BookmarkData::m_nearestToponym) + .def_readwrite("properties", &BookmarkData::m_properties) .def("__eq__", &BookmarkData::operator==) .def("__ne__", &BookmarkData::operator!=) .def("__str__", &BookmarkDataToString); @@ -782,6 +801,9 @@ BOOST_PYTHON_MODULE(pykmlib) .def_readwrite("timestamp", &TrackData::m_timestamp) .def_readwrite("layers", &TrackData::m_layers) .def_readwrite("points", &TrackData::m_points) + .def_readwrite("visible", &TrackData::m_visible) + .def_readwrite("nearest_toponyms", &TrackData::m_nearestToponyms) + .def_readwrite("properties", &TrackData::m_properties) .def("__eq__", &TrackData::operator==) .def("__ne__", &TrackData::operator!=) .def("__str__", &TrackDataToString); @@ -792,16 +814,6 @@ BOOST_PYTHON_MODULE(pykmlib) .def("set_list", &SetLanguages) .def("__str__", &LanguagesListToString); - class_("Properties") - .def("__len__", &Properties::size) - .def("clear", &Properties::clear) - .def("__getitem__", &PropertiesAdapter::Get, return_value_policy()) - .def("__setitem__", &PropertiesAdapter::Set, with_custodian_and_ward<1,2>()) - .def("__delitem__", &PropertiesAdapter::Delete) - .def("get_dict", &PropertiesAdapter::GetDict) - .def("set_dict", &PropertiesAdapter::SetDict) - .def("__str__", &PropertiesAdapter::ToString); - class_("CategoryData") .def_readwrite("name", &CategoryData::m_name) .def_readwrite("annotation", &CategoryData::m_annotation) @@ -815,7 +827,7 @@ BOOST_PYTHON_MODULE(pykmlib) .def_readwrite("reviews_number", &CategoryData::m_reviewsNumber) .def_readwrite("access_rules", &CategoryData::m_accessRules) .def_readwrite("tags", &CategoryData::m_tags) - .def_readwrite("cities", &CategoryData::m_cities) + .def_readwrite("toponyms", &CategoryData::m_toponyms) .def_readwrite("languages", &CategoryData::m_languageCodes) .def_readwrite("properties", &CategoryData::m_properties) .def("__eq__", &CategoryData::operator==) diff --git a/kml/pykmlib/bindings_test.py b/kml/pykmlib/bindings_test.py index 66a4413cbf..524f9dae22 100644 --- a/kml/pykmlib/bindings_test.py +++ b/kml/pykmlib/bindings_test.py @@ -31,7 +31,7 @@ class PyKmlibAdsTest(unittest.TestCase): category.last_modified = int(datetime.datetime.now().timestamp()) category.access_rules = pykmlib.AccessRules.PUBLIC category.tags.set_list(['mountains', 'ski', 'snowboard']) - category.cities.set_list([pykmlib.LatLon(35.2424, 56.2164), pykmlib.LatLon(34.2443, 46.3536)]) + category.toponyms.set_list(['12345', '54321']) category.languages.set_list(['en', 'ru', 'de']) category.properties.set_dict({'property1':'value1', 'property2':'value2'}) @@ -50,7 +50,10 @@ class PyKmlibAdsTest(unittest.TestCase): bookmark.icon = pykmlib.BookmarkIcon.HOTEL bookmark.viewport_scale = 15 bookmark.timestamp = int(datetime.datetime.now().timestamp()) - bookmark.point = pykmlib.LatLon(45.9242, 56.8679) + bookmark.point = pykmlib.LatLon(45.9242, 56.8679) + bookmark.visible = True + bookmark.nearest_toponym = '12345' + bookmark.properties.set_dict({'bm_property1':'value1', 'bm_property2':'value2'}) bookmark.bound_tracks.set_list([0]) layer1 = pykmlib.TrackLayer() @@ -72,6 +75,9 @@ class PyKmlibAdsTest(unittest.TestCase): pykmlib.LatLon(45.9242, 56.8679), pykmlib.LatLon(45.2244, 56.2786), pykmlib.LatLon(45.1964, 56.9832)]) + track.visible = True + track.nearest_toponyms.set_list(['12345', '54321', '98765']) + track.properties.set_dict({'tr_property1':'value1', 'tr_property2':'value2'}) file_data = pykmlib.FileData() file_data.server_id = 'AAAA-BBBB-CCCC-DDDD' diff --git a/kml/serdes.cpp b/kml/serdes.cpp index 173c9b7b50..daa131f416 100644 --- a/kml/serdes.cpp +++ b/kml/serdes.cpp @@ -217,23 +217,6 @@ void SaveStringsArray(KmlWriter::WriterWrapper & writer, writer << offsetStr << "\n"; } -void SavePointsArray(KmlWriter::WriterWrapper & writer, - std::vector const & pointsArray, - std::string const & tagName, std::string const & offsetStr) -{ - if (pointsArray.empty()) - return; - - writer << offsetStr << "\n"; - for (auto const & p : pointsArray) - { - writer << offsetStr << kIndent2 << ""; - writer << PointToString(p); - writer << "\n"; - } - writer << offsetStr << "\n"; -} - void SaveStringsMap(KmlWriter::WriterWrapper & writer, std::map const & stringsMap, std::string const & tagName, std::string const & offsetStr) @@ -297,7 +280,7 @@ void SaveCategoryExtendedData(KmlWriter::WriterWrapper & writer, CategoryData co SaveStringsArray(writer, categoryData.m_tags, "tags", kIndent4); - SavePointsArray(writer, categoryData.m_cities, "cities", kIndent4); + SaveStringsArray(writer, categoryData.m_toponyms, "toponyms", kIndent4); std::vector languageCodes; languageCodes.reserve(categoryData.m_languageCodes.size()); @@ -338,17 +321,12 @@ void SaveCategoryData(KmlWriter::WriterWrapper & writer, CategoryData const & ca void SaveBookmarkExtendedData(KmlWriter::WriterWrapper & writer, BookmarkData const & bookmarkData) { - if (bookmarkData.m_name.empty() && bookmarkData.m_description.empty() && - bookmarkData.m_customName.empty() && bookmarkData.m_viewportScale == 0 && - bookmarkData.m_icon == BookmarkIcon::None && bookmarkData.m_featureTypes.empty() && - bookmarkData.m_boundTracks.empty()) - { - return; - } - writer << kIndent4 << kExtendedDataHeader; - SaveLocalizableString(writer, bookmarkData.m_name, "name", kIndent6); - SaveLocalizableString(writer, bookmarkData.m_description, "description", kIndent6); + if (!bookmarkData.m_name.empty()) + SaveLocalizableString(writer, bookmarkData.m_name, "name", kIndent6); + + if (!bookmarkData.m_description.empty()) + SaveLocalizableString(writer, bookmarkData.m_description, "description", kIndent6); if (!bookmarkData.m_featureTypes.empty()) { @@ -371,14 +349,29 @@ void SaveBookmarkExtendedData(KmlWriter::WriterWrapper & writer, BookmarkData co auto const scale = strings::to_string(static_cast(bookmarkData.m_viewportScale)); writer << kIndent6 << "" << scale << "\n"; } + if (bookmarkData.m_icon != BookmarkIcon::None) writer << kIndent6 << "" << DebugPrint(bookmarkData.m_icon) << "\n"; - std::vector boundTracks; - boundTracks.reserve(bookmarkData.m_boundTracks.size()); - for (auto const & t : bookmarkData.m_boundTracks) - boundTracks.push_back(strings::to_string(static_cast(t))); - SaveStringsArray(writer, boundTracks, "boundTracks", kIndent6); + if (!bookmarkData.m_boundTracks.empty()) + { + std::vector boundTracks; + boundTracks.reserve(bookmarkData.m_boundTracks.size()); + for (auto const & t : bookmarkData.m_boundTracks) + boundTracks.push_back(strings::to_string(static_cast(t))); + SaveStringsArray(writer, boundTracks, "boundTracks", kIndent6); + } + + writer << kIndent6 << "" << (bookmarkData.m_visible ? "1" : "0") << "\n"; + + if (!bookmarkData.m_nearestToponym.empty()) + { + writer << kIndent6 << ""; + SaveStringWithCDATA(writer, bookmarkData.m_nearestToponym); + writer << "\n"; + } + + SaveStringsMap(writer, bookmarkData.m_properties, "properties", kIndent6); writer << kIndent4 << kExtendedDataFooter; } @@ -441,6 +434,11 @@ void SaveTrackExtendedData(KmlWriter::WriterWrapper & writer, TrackData const & } writer << kIndent6 << "\n"; + writer << kIndent6 << "" << (trackData.m_visible ? "1" : "0") << "\n"; + + SaveStringsArray(writer, trackData.m_nearestToponyms, "nearestToponyms", kIndent6); + SaveStringsMap(writer, trackData.m_properties, "properties", kIndent6); + writer << kIndent4 << kExtendedDataFooter; } @@ -535,6 +533,10 @@ void KmlParser::ResetPoint() m_featureTypes.clear(); m_customName.clear(); m_boundTracks.clear(); + m_visible = true; + m_nearestToponym.clear(); + m_nearestToponyms.clear(); + m_properties.clear(); m_localId = 0; m_trackLayers.clear(); m_trackWidth = kDefaultTrackWidth; @@ -713,6 +715,9 @@ void KmlParser::Pop(std::string const & tag) data.m_featureTypes = std::move(m_featureTypes); data.m_customName = std::move(m_customName); data.m_boundTracks = std::move(m_boundTracks); + data.m_visible = m_visible; + data.m_nearestToponym = std::move(m_nearestToponym); + data.m_properties = std::move(m_properties); // Here we set custom name from 'name' field for KML-files exported from 3rd-party services. if (data.m_name.size() == 1 && data.m_name.begin()->first == kDefaultLangCode && @@ -732,6 +737,9 @@ void KmlParser::Pop(std::string const & tag) data.m_layers = std::move(m_trackLayers); data.m_timestamp = m_timestamp; data.m_points = m_points; + data.m_visible = m_visible; + data.m_nearestToponyms = std::move(m_nearestToponyms); + data.m_properties = std::move(m_properties); m_data.m_tracksData.push_back(std::move(data)); } } @@ -855,11 +863,9 @@ void KmlParser::CharData(std::string value) { m_data.m_categoryData.m_tags.push_back(value); } - else if (prevTag == "mwm:cities") + else if (prevTag == "mwm:toponyms") { - m2::PointD pt; - if (ParsePoint(value, ", \n\r\t", pt)) - m_data.m_categoryData.m_cities.push_back(pt); + m_data.m_categoryData.m_toponyms.push_back(value); } else if (prevTag == "mwm:languageCodes") { @@ -980,6 +986,14 @@ void KmlParser::CharData(std::string value) { m_icon = GetIcon(value); } + else if (currTag == "mwm:visibility") + { + m_visible = value != "0"; + } + else if (currTag == "mwm:nearestToponym") + { + m_nearestToponym = value; + } } else if (prevTag == "TimeStamp") { @@ -1049,7 +1063,18 @@ void KmlParser::CharData(std::string value) } } else if (prevTag == "mwm:boundTracks" && strings::to_uint(value, i)) + { m_boundTracks.push_back(static_cast(i)); + } + else if (prevTag == "mwm:nearestToponyms") + { + m_nearestToponyms.push_back(value); + } + else if (prevTag == "mwm:properties" && !m_attrKey.empty()) + { + m_properties[m_attrKey] = value; + m_attrKey.clear(); + } } } } diff --git a/kml/serdes.hpp b/kml/serdes.hpp index f30531f850..4343822ef7 100644 --- a/kml/serdes.hpp +++ b/kml/serdes.hpp @@ -122,6 +122,10 @@ private: LocalId m_localId; BookmarkIcon m_icon; std::vector m_trackLayers; + bool m_visible; + std::string m_nearestToponym; + std::vector m_nearestToponyms; + kml::Properties m_properties; double m_trackWidth; }; diff --git a/kml/serdes_binary.hpp b/kml/serdes_binary.hpp index b6169ca6eb..61c08d749f 100644 --- a/kml/serdes_binary.hpp +++ b/kml/serdes_binary.hpp @@ -2,6 +2,7 @@ #include "kml/header_binary.hpp" #include "kml/types.hpp" +#include "kml/types_v3.hpp" #include "kml/visitors.hpp" #include "coding/read_write_utils.hpp" @@ -23,7 +24,8 @@ enum class Version : uint8_t 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). - Latest = V3 + V4 = 4, // 26th August 2019 (key-value properties and nearestToponym for bookmarks and tracks, cities -> toponyms) + Latest = V4 }; class SerializerKml @@ -135,72 +137,38 @@ public: NonOwningReaderSource source(reader); auto const v = ReadPrimitiveFromSource(source); - if (v != Version::Latest && v != Version::V2) + if (v != Version::Latest && v != Version::V2 && v != Version::V3) MYTHROW(DeserializeException, ("Incorrect file version.")); - // Read device id. - { - auto const sz = ReadVarUint(source); - m_data.m_deviceId.resize(sz); - source.Read(&m_data.m_deviceId[0], sz); - } - - // Read server id. - { - auto const sz = ReadVarUint(source); - m_data.m_serverId.resize(sz); - source.Read(&m_data.m_serverId[0], sz); - } - - // Read bits count in double number. - m_doubleBits = ReadPrimitiveFromSource(source); - if (m_doubleBits == 0 || m_doubleBits > 32) - MYTHROW(DeserializeException, ("Incorrect double bits count: ", m_doubleBits)); + ReadDeviceId(source); + ReadServerId(source); + ReadBitsCountInDouble(source); auto subReader = reader.CreateSubReader(source.Pos(), source.Size()); InitializeIfNeeded(*subReader); - // Deserialize category. + if (v == Version::V4) { - auto categorySubReader = CreateCategorySubReader(*subReader); - NonOwningReaderSource src(*categorySubReader); - CategoryDeserializerVisitor visitor(src, m_doubleBits); - visitor(m_data.m_categoryData); + DeserializeCategory(subReader, m_data); + DeserializeBookmarks(subReader, m_data); + DeserializeTracks(subReader, m_data); + DeserializeStrings(subReader, m_data); } - - // Deserialize bookmarks. + else { - auto bookmarkSubReader = CreateBookmarkSubReader(*subReader); - NonOwningReaderSource src(*bookmarkSubReader); - BookmarkDeserializerVisitor visitor(src, m_doubleBits); - visitor(m_data.m_bookmarksData); - } + // NOTE: v.2 and v.3 are binary compatible. + FileDataV3 dataV3; + DeserializeCategory(subReader, dataV3); + DeserializeBookmarks(subReader, dataV3); - // Migrate bookmarks. - if (v == Version::V2) - { - for (auto & data : m_data.m_bookmarksData) - data.m_featureTypes.clear(); - } + // Migrate bookmarks (it's necessary ony for v.2). + if (v == Version::V2) + MigrateBookmarksV2(dataV3); - // Deserialize tracks. - { - auto trackSubReader = CreateTrackSubReader(*subReader); - NonOwningReaderSource src(*trackSubReader); - BookmarkDeserializerVisitor visitor(src, m_doubleBits); - visitor(m_data.m_tracksData); - } + DeserializeTracks(subReader, dataV3); + DeserializeStrings(subReader, dataV3); - // Deserialize strings. - { - auto textsSubReader = CreateStringsSubReader(*subReader); - coding::BlockedTextStorage strings(*textsSubReader); - DeserializedStringCollector collector(strings); - CollectorVisitor visitor(collector); - m_data.Visit(visitor); - CollectorVisitor clearVisitor(collector, - true /* clear index */); - m_data.Visit(clearVisitor); + m_data = dataV3.ConvertToLatestVersion(); } } @@ -250,6 +218,73 @@ private: return CreateSubReader(reader, m_header.m_stringsOffset, m_header.m_eosOffset); } + void ReadDeviceId(NonOwningReaderSource & source) + { + auto const sz = ReadVarUint(source); + m_data.m_deviceId.resize(sz); + source.Read(&m_data.m_deviceId[0], sz); + } + + void ReadServerId(NonOwningReaderSource & source) + { + auto const sz = ReadVarUint(source); + m_data.m_serverId.resize(sz); + source.Read(&m_data.m_serverId[0], sz); + } + + void ReadBitsCountInDouble(NonOwningReaderSource & source) + { + m_doubleBits = ReadPrimitiveFromSource(source); + if (m_doubleBits == 0 || m_doubleBits > 32) + MYTHROW(DeserializeException, ("Incorrect double bits count: ", m_doubleBits)); + } + + template + void DeserializeCategory(std::unique_ptr & subReader, FileDataType & data) + { + auto categorySubReader = CreateCategorySubReader(*subReader); + NonOwningReaderSource src(*categorySubReader); + CategoryDeserializerVisitor visitor(src, m_doubleBits); + visitor(data.m_categoryData); + } + + template + void DeserializeBookmarks(std::unique_ptr & subReader, FileDataType & data) + { + auto bookmarkSubReader = CreateBookmarkSubReader(*subReader); + NonOwningReaderSource src(*bookmarkSubReader); + BookmarkDeserializerVisitor visitor(src, m_doubleBits); + visitor(data.m_bookmarksData); + } + + void MigrateBookmarksV2(FileDataV3 & data) + { + for (auto & d : data.m_bookmarksData) + d.m_featureTypes.clear(); + } + + template + void DeserializeTracks(std::unique_ptr & subReader, FileDataType & data) + { + auto trackSubReader = CreateTrackSubReader(*subReader); + NonOwningReaderSource src(*trackSubReader); + BookmarkDeserializerVisitor visitor(src, m_doubleBits); + visitor(data.m_tracksData); + } + + template + void DeserializeStrings(std::unique_ptr & subReader, FileDataType & data) + { + auto textsSubReader = CreateStringsSubReader(*subReader); + coding::BlockedTextStorage strings(*textsSubReader); + DeserializedStringCollector collector(strings); + CollectorVisitor visitor(collector); + data.Visit(visitor); + CollectorVisitor clearVisitor(collector, + true /* clear index */); + data.Visit(clearVisitor); + } + FileData & m_data; Header m_header; uint8_t m_doubleBits = 0; diff --git a/kml/types.hpp b/kml/types.hpp index 44840fb553..bf8e25a696 100644 --- a/kml/types.hpp +++ b/kml/types.hpp @@ -163,9 +163,13 @@ struct BookmarkData visitor(m_timestamp, "timestamp"), visitor(m_point, "point"), visitor(m_boundTracks, "boundTracks"), + visitor(m_visible, "visible"), + visitor(m_nearestToponym, "nearestToponym"), + visitor(m_properties, "properties"), VISITOR_COLLECTABLE) - DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description, m_customName) + DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description, m_customName, + m_nearestToponym, m_properties) bool operator==(BookmarkData const & data) const { @@ -178,7 +182,10 @@ struct BookmarkData m_point.EqualDxDy(data.m_point, kEps) && m_featureTypes == data.m_featureTypes && m_customName == data.m_customName && - m_boundTracks == data.m_boundTracks; + m_boundTracks == data.m_boundTracks && + m_visible == data.m_visible && + m_nearestToponym == data.m_nearestToponym && + m_properties == data.m_properties; } bool operator!=(BookmarkData const & data) const { return !operator==(data); } @@ -206,6 +213,12 @@ struct BookmarkData m2::PointD m_point; // Bound tracks (vector contains local track ids). std::vector m_boundTracks; + // Visibility. + bool m_visible = true; + // Nearest toponym. + std::string m_nearestToponym; + // Key-value properties. + Properties m_properties; }; struct TrackLayer @@ -236,15 +249,20 @@ struct TrackData 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) + DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description, m_nearestToponyms, m_properties) bool operator==(TrackData 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); + 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!=(TrackData const & data) const { return !operator==(data); } @@ -263,6 +281,12 @@ struct TrackData 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 CategoryData @@ -280,13 +304,13 @@ struct CategoryData visitor(m_lastModified, "lastModified"), visitor(m_accessRules, "accessRules"), visitor(m_tags, "tags"), - visitor(m_cities, "cities"), + visitor(m_toponyms, "toponyms"), visitor(m_languageCodes, "languageCodes"), visitor(m_properties, "properties"), VISITOR_COLLECTABLE) DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_annotation, m_description, - m_imageUrl, m_authorName, m_authorId, m_tags, m_properties) + m_imageUrl, m_authorName, m_authorId, m_tags, m_toponyms, m_properties) bool operator==(CategoryData const & data) const { @@ -297,7 +321,7 @@ struct CategoryData m_authorName == data.m_authorName && m_authorId == data.m_authorId && fabs(m_rating - data.m_rating) < kEps && m_reviewsNumber == data.m_reviewsNumber && IsEqual(m_lastModified, data.m_lastModified) && m_tags == data.m_tags && - IsEqual(m_cities, data.m_cities) && m_languageCodes == data.m_languageCodes && + m_toponyms == data.m_toponyms && m_languageCodes == data.m_languageCodes && m_properties == data.m_properties; } @@ -329,8 +353,8 @@ struct CategoryData AccessRules m_accessRules = AccessRules::Local; // Collection of tags. std::vector m_tags; - // Collection of cities coordinates. - std::vector m_cities; + // Collection of geo ids for relevant toponyms. + std::vector m_toponyms; // Language codes. std::vector m_languageCodes; // Key-value properties. diff --git a/kml/types_v3.hpp b/kml/types_v3.hpp new file mode 100644 index 0000000000..648869aeff --- /dev/null +++ b/kml/types_v3.hpp @@ -0,0 +1,279 @@ +#pragma once + +#include "kml/types.hpp" + +#include "base/visitor.hpp" + +#include +#include +#include +#include +#include +#include + +namespace kml +{ +struct BookmarkDataV3 +{ + DECLARE_VISITOR_AND_DEBUG_PRINT(BookmarkDataV3, visitor(m_id, "id"), + visitor(m_name, "name"), + visitor(m_description, "description"), + visitor(m_featureTypes, "featureTypes"), + visitor(m_customName, "customName"), + visitor(m_color, "color"), + visitor(m_icon, "icon"), + visitor(m_viewportScale, "viewportScale"), + visitor(m_timestamp, "timestamp"), + visitor(m_point, "point"), + visitor(m_boundTracks, "boundTracks"), + VISITOR_COLLECTABLE) + + DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description, m_customName) + + bool operator==(BookmarkDataV3 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_featureTypes == data.m_featureTypes && + m_customName == data.m_customName && + m_boundTracks == data.m_boundTracks; + } + + bool operator!=(BookmarkDataV3 const & data) const { return !operator==(data); } + + BookmarkData ConvertToLatestVersion() const + { + BookmarkData data; + data.m_id = m_id; + data.m_name = m_name; + data.m_description = m_description; + data.m_featureTypes = m_featureTypes; + data.m_customName = m_customName; + data.m_color = m_color; + data.m_icon = m_icon; + data.m_viewportScale = m_viewportScale; + data.m_timestamp = m_timestamp; + data.m_point = m_point; + data.m_boundTracks = m_boundTracks; + return data; + } + + // Unique id (it will not be serialized in text files). + MarkId m_id = kInvalidMarkId; + // Bookmark's name. + LocalizableString m_name; + // Bookmark's description. + LocalizableString m_description; + // Bound feature's types. + std::vector m_featureTypes; + // Custom bookmark's name. + LocalizableString m_customName; + // Bookmark's color. + ColorData m_color; + // Bookmark's icon. + BookmarkIcon m_icon = BookmarkIcon::None; + // Viewport scale. 0 is a default value (no scale set). + uint8_t m_viewportScale = 0; + // Creation timestamp. + Timestamp m_timestamp = {}; + // Coordinates in mercator. + m2::PointD m_point; + // Bound tracks (vector contains local track ids). + std::vector m_boundTracks; +}; + +struct TrackDataV3 +{ + DECLARE_VISITOR_AND_DEBUG_PRINT(TrackDataV3, 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_COLLECTABLE) + + DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description) + + bool operator==(TrackDataV3 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); + } + + bool operator!=(TrackDataV3 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; + data.m_points = m_points; + 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; +}; + +struct CategoryDataV3 +{ + DECLARE_VISITOR_AND_DEBUG_PRINT(CategoryDataV3, visitor(m_id, "id"), + visitor(m_name, "name"), + visitor(m_imageUrl, "imageUrl"), + visitor(m_annotation, "annotation"), + visitor(m_description, "description"), + visitor(m_visible, "visible"), + visitor(m_authorName, "authorName"), + visitor(m_authorId, "authorId"), + visitor(m_rating, "rating"), + visitor(m_reviewsNumber, "reviewsNumber"), + visitor(m_lastModified, "lastModified"), + visitor(m_accessRules, "accessRules"), + visitor(m_tags, "tags"), + visitor(m_cities, "cities"), + visitor(m_languageCodes, "languageCodes"), + visitor(m_properties, "properties"), + VISITOR_COLLECTABLE) + + DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_annotation, m_description, + m_imageUrl, m_authorName, m_authorId, m_tags, m_properties) + + bool operator==(CategoryDataV3 const & data) const + { + double constexpr kEps = 1e-5; + return m_id == data.m_id && m_name == data.m_name && m_imageUrl == data.m_imageUrl && + m_annotation == data.m_annotation && m_description == data.m_description && + m_visible == data.m_visible && m_accessRules == data.m_accessRules && + m_authorName == data.m_authorName && m_authorId == data.m_authorId && + fabs(m_rating - data.m_rating) < kEps && m_reviewsNumber == data.m_reviewsNumber && + IsEqual(m_lastModified, data.m_lastModified) && m_tags == data.m_tags && + IsEqual(m_cities, data.m_cities) && m_languageCodes == data.m_languageCodes && + m_properties == data.m_properties; + } + + bool operator!=(CategoryDataV3 const & data) const { return !operator==(data); } + + CategoryData ConvertToLatestVersion() const + { + // We don't convert m_cities to m_toponyms, since m_cities have been never used. + CategoryData data; + data.m_id = kInvalidMarkGroupId; + data.m_name = m_name; + data.m_imageUrl = m_imageUrl; + data.m_annotation = m_annotation; + data.m_description = m_description; + data.m_visible = m_visible; + data.m_authorName = m_authorName; + data.m_authorId = m_authorId; + data.m_lastModified = m_lastModified; + data.m_rating = m_rating; + data.m_reviewsNumber = m_reviewsNumber; + data.m_accessRules = m_accessRules; + data.m_tags = m_tags; + data.m_languageCodes = m_languageCodes; + data.m_properties = m_properties; + return data; + } + + // Unique id (it will not be serialized in text files). + MarkGroupId m_id = kInvalidMarkGroupId; + // Category's name. + LocalizableString m_name; + // Image URL. + std::string m_imageUrl; + // Category's description. + LocalizableString m_annotation; + // Category's description. + LocalizableString m_description; + // Collection visibility. + bool m_visible = true; + // Author's name. + std::string m_authorName; + // Author's id. + std::string m_authorId; + // Last modification timestamp. + Timestamp m_lastModified; + // Rating. + double m_rating = 0.0; + // Number of reviews. + uint32_t m_reviewsNumber = 0; + // Access rules. + AccessRules m_accessRules = AccessRules::Local; + // Collection of tags. + std::vector m_tags; + // Collection of cities coordinates. + std::vector m_cities; + // Language codes. + std::vector m_languageCodes; + // Key-value properties. + Properties m_properties; +}; + +struct FileDataV3 +{ + DECLARE_VISITOR_AND_DEBUG_PRINT(FileDataV3, visitor(m_serverId, "serverId"), + visitor(m_categoryData, "category"), + visitor(m_bookmarksData, "bookmarks"), + visitor(m_tracksData, "tracks")) + + bool operator==(FileDataV3 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!=(FileDataV3 const & data) const { return !operator==(data); } + + // Device id (it will not be serialized in text files). + std::string m_deviceId; + // Server id. + std::string m_serverId; + // Category's data. + CategoryDataV3 m_categoryData; + // Bookmarks collection. + std::vector m_bookmarksData; + // Tracks collection. + std::vector m_tracksData; + + FileData ConvertToLatestVersion() const + { + FileData data; + data.m_deviceId = m_deviceId; + data.m_serverId = m_serverId; + data.m_categoryData = m_categoryData.ConvertToLatestVersion(); + + data.m_bookmarksData.reserve(m_bookmarksData.size()); + for (auto const & d : m_bookmarksData) + data.m_bookmarksData.emplace_back(d.ConvertToLatestVersion()); + + data.m_tracksData.reserve(m_tracksData.size()); + for (auto const & d : m_tracksData) + data.m_tracksData.emplace_back(d.ConvertToLatestVersion()); + + return data; + } +}; +} // namespace kml diff --git a/kml/visitors.hpp b/kml/visitors.hpp index 4670a57fd7..fa51436e05 100644 --- a/kml/visitors.hpp +++ b/kml/visitors.hpp @@ -38,7 +38,11 @@ class CollectorVisitor enum {value = std::is_same::value || std::is_same::value || std::is_same::value || - 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: