diff options
author | Roman Kuznetsov <r.kuznetsow@gmail.com> | 2018-03-27 16:22:29 +0300 |
---|---|---|
committer | Daria Volvenkova <d.volvenkova@corp.mail.ru> | 2018-03-27 16:22:29 +0300 |
commit | c945ce89d964c6cae62378a26e938b46c7d2ec15 (patch) | |
tree | 0acf119f7d4663a1891deafe8b7fb23aafa7363f /kml | |
parent | d9d795a2e781360ef4f1c9917eefb20fde9abe8c (diff) |
Added new data in extended KML format (#8388)
Added new data in extended KML format
Diffstat (limited to 'kml')
-rw-r--r-- | kml/kml_tests/serdes_tests.cpp | 126 | ||||
-rw-r--r-- | kml/serdes.cpp | 139 | ||||
-rw-r--r-- | kml/serdes.hpp | 4 | ||||
-rw-r--r-- | kml/type_utils.hpp | 5 | ||||
-rw-r--r-- | kml/types.hpp | 48 | ||||
-rw-r--r-- | kml/visitors.hpp | 81 |
6 files changed, 314 insertions, 89 deletions
diff --git a/kml/kml_tests/serdes_tests.cpp b/kml/kml_tests/serdes_tests.cpp index dff0f6f0ac..880d47262e 100644 --- a/kml/kml_tests/serdes_tests.cpp +++ b/kml/kml_tests/serdes_tests.cpp @@ -123,41 +123,42 @@ char const * kTextKml = "</kml>"; std::vector<uint8_t> const kBinKml = { - 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x2C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xAB, 0xEB, 0x96, 0xE2, 0xFE, 0x83, 0xD4, 0x11, - 0x00, 0x02, 0x01, 0x00, 0x03, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xC2, 0xFB, 0xF9, 0x01, 0x9E, 0xD0, 0xC7, 0x95, - 0x9B, 0x9D, 0xBD, 0xAB, 0x12, 0x00, 0x02, 0x01, 0x00, 0x04, 0x01, 0x00, 0x05, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0xBC, - 0xED, 0xA7, 0x03, 0xEA, 0xA4, 0xD0, 0x9C, 0xD9, 0xA7, 0xBE, 0x9A, 0x19, 0x00, 0x02, 0x01, 0x00, - 0x06, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xE4, 0xAF, 0xA7, 0xC1, 0xEB, 0xA6, 0xBE, 0x9A, 0x19, 0x00, 0x02, 0x01, - 0x00, 0x07, 0x01, 0x00, 0x08, 0x00, 0x54, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x0E, 0x08, 0x1B, 0x1A, 0x41, 0x0C, 0x11, 0x25, 0x3F, 0x00, 0x02, 0x00, 0x01, 0x08, 0x03, 0x02, - 0x06, 0x01, 0x03, 0x06, 0x40, 0x05, 0x07, 0x8D, 0x01, 0x06, 0x05, 0x02, 0x07, 0x07, 0x71, 0x09, - 0x05, 0x05, 0x0A, 0x07, 0x12, 0x0B, 0x06, 0x5E, 0x0D, 0x06, 0xBA, 0x01, 0x0E, 0x05, 0x04, 0x0F, - 0x05, 0x75, 0x11, 0x05, 0x0D, 0x12, 0x06, 0x09, 0x13, 0x07, 0x4E, 0x15, 0x06, 0x91, 0x01, 0x16, - 0x06, 0x0B, 0x17, 0x06, 0x73, 0x19, 0x05, 0x06, 0x1A, 0x08, 0x1C, 0x1B, 0x07, 0x63, 0x1D, 0x06, - 0xC0, 0x01, 0x1E, 0x05, 0x07, 0x1F, 0x06, 0x77, 0x21, 0x06, 0x0A, 0x22, 0x07, 0x08, 0x23, 0x07, - 0x49, 0x25, 0x07, 0x90, 0x01, 0x27, 0x06, 0x72, 0x2A, 0x08, 0x14, 0x2B, 0x06, 0x62, 0x2D, 0x06, - 0xBD, 0x01, 0x32, 0x06, 0x0C, 0x33, 0x06, 0x57, 0x35, 0x07, 0xA1, 0x01, 0x36, 0x06, 0x0F, 0x37, - 0x07, 0x76, 0x3A, 0x08, 0x31, 0x3B, 0x06, 0x68, 0x3D, 0x06, 0xD1, 0x01, 0x3F, 0x06, 0x79, 0x41, - 0x07, 0xB8, 0x01, 0x45, 0x07, 0x8F, 0x01, 0x47, 0x07, 0x74, 0x4A, 0x07, 0x13, 0x53, 0x07, 0x59, - 0x5A, 0x07, 0x29, 0x5B, 0x07, 0x6D, 0x62, 0x08, 0x10, 0x63, 0x07, 0x4B, 0x65, 0x07, 0x96, 0x01, - 0x6A, 0x08, 0x18, 0x75, 0x07, 0xB7, 0x01, 0x77, 0x07, 0x8A, 0x01, 0x7A, 0x08, 0x3F, 0x81, 0x01, - 0x08, 0x0E, 0x9A, 0x01, 0x08, 0x25, 0xAA, 0x01, 0x08, 0x17, 0xBA, 0x01, 0x08, 0x3E, 0xE2, 0x01, - 0x08, 0x11, 0xEA, 0x01, 0x08, 0x19, 0xFA, 0x01, 0x08, 0x41, 0xB0, 0x01, 0xD7, 0x64, 0xD6, 0x6E, - 0x37, 0x46, 0x07, 0x3D, 0xA1, 0xA8, 0x65, 0x2F, 0x34, 0x01, 0x83, 0xBE, 0x2E, 0x87, 0x3C, 0x64, - 0x71, 0x6E, 0xA3, 0xE7, 0xCC, 0xB7, 0x8E, 0x6D, 0xA7, 0xE2, 0xFD, 0x13, 0xBC, 0x9A, 0x51, 0x7D, - 0xF2, 0xE9, 0x17, 0x8F, 0x33, 0x91, 0x28, 0xDB, 0xA1, 0x22, 0x5F, 0xB3, 0x14, 0x65, 0xE9, 0x57, - 0x92, 0xAC, 0x33, 0xC5, 0xF8, 0x3E, 0x37, 0x35, 0x00, 0x88, 0x1C, 0x96, 0x62, 0x97, 0x17, 0x09, - 0xA5, 0x3F, 0x42, 0x18, 0x98, 0xD5, 0x45, 0x96, 0x1D, 0x0A, 0x46, 0x2C, 0xB9, 0x94, 0xFE, 0x35, - 0xF7, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xD0, 0xA2, 0xD9, 0x95, 0x6B, 0xDC, 0x69, - 0xEA, 0x2D, 0x52, 0xB0, 0x43, 0xA8, 0x7C, 0xA9, 0x32, 0x0E, 0x01, 0x00, 0x09, + 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3A, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xE2, 0xAB, 0xEB, 0x96, 0xE2, 0xFE, 0x83, 0xD4, 0x11, 0x00, 0x03, 0x01, 0x00, 0x03, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0xC2, 0xFB, 0xF9, 0x01, 0x9E, 0xD0, 0xC7, 0x95, 0x9B, 0x9D, 0xBD, 0xAB, 0x12, + 0x00, 0x03, 0x01, 0x00, 0x04, 0x01, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0xBC, 0xED, 0xA7, 0x03, 0xEA, + 0xA4, 0xD0, 0x9C, 0xD9, 0xA7, 0xBE, 0x9A, 0x19, 0x00, 0x03, 0x01, 0x00, 0x06, 0x00, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xE4, 0xAF, 0xA7, 0xC1, 0xEB, 0xA6, 0xBE, 0x9A, 0x19, 0x00, 0x03, 0x01, 0x00, 0x07, 0x01, + 0x00, 0x08, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0E, 0x08, + 0x1B, 0x1A, 0x41, 0x0C, 0x11, 0x25, 0x3F, 0x00, 0x02, 0x00, 0x01, 0x08, 0x03, 0x02, 0x06, 0x01, + 0x03, 0x06, 0x40, 0x05, 0x07, 0x8D, 0x01, 0x06, 0x05, 0x02, 0x07, 0x07, 0x71, 0x09, 0x05, 0x05, + 0x0A, 0x07, 0x12, 0x0B, 0x06, 0x5E, 0x0D, 0x06, 0xBA, 0x01, 0x0E, 0x05, 0x04, 0x0F, 0x05, 0x75, + 0x11, 0x05, 0x0D, 0x12, 0x06, 0x09, 0x13, 0x07, 0x4E, 0x15, 0x06, 0x91, 0x01, 0x16, 0x06, 0x0B, + 0x17, 0x06, 0x73, 0x19, 0x05, 0x06, 0x1A, 0x08, 0x1C, 0x1B, 0x07, 0x63, 0x1D, 0x06, 0xC0, 0x01, + 0x1E, 0x05, 0x07, 0x1F, 0x06, 0x77, 0x21, 0x06, 0x0A, 0x22, 0x07, 0x08, 0x23, 0x07, 0x49, 0x25, + 0x07, 0x90, 0x01, 0x27, 0x06, 0x72, 0x2A, 0x08, 0x14, 0x2B, 0x06, 0x62, 0x2D, 0x06, 0xBD, 0x01, + 0x32, 0x06, 0x0C, 0x33, 0x06, 0x57, 0x35, 0x07, 0xA1, 0x01, 0x36, 0x06, 0x0F, 0x37, 0x07, 0x76, + 0x3A, 0x08, 0x31, 0x3B, 0x06, 0x68, 0x3D, 0x06, 0xD1, 0x01, 0x3F, 0x06, 0x79, 0x41, 0x07, 0xB8, + 0x01, 0x45, 0x07, 0x8F, 0x01, 0x47, 0x07, 0x74, 0x4A, 0x07, 0x13, 0x53, 0x07, 0x59, 0x5A, 0x07, + 0x29, 0x5B, 0x07, 0x6D, 0x62, 0x08, 0x10, 0x63, 0x07, 0x4B, 0x65, 0x07, 0x96, 0x01, 0x6A, 0x08, + 0x18, 0x75, 0x07, 0xB7, 0x01, 0x77, 0x07, 0x8A, 0x01, 0x7A, 0x08, 0x3F, 0x81, 0x01, 0x08, 0x0E, + 0x9A, 0x01, 0x08, 0x25, 0xAA, 0x01, 0x08, 0x17, 0xBA, 0x01, 0x08, 0x3E, 0xE2, 0x01, 0x08, 0x11, + 0xEA, 0x01, 0x08, 0x19, 0xFA, 0x01, 0x08, 0x41, 0xB0, 0x01, 0xD7, 0x64, 0xD6, 0x6E, 0x37, 0x46, + 0x07, 0x3D, 0xA1, 0xA8, 0x65, 0x2F, 0x34, 0x01, 0x83, 0xBE, 0x2E, 0x87, 0x3C, 0x64, 0x71, 0x6E, + 0xA3, 0xE7, 0xCC, 0xB7, 0x8E, 0x6D, 0xA7, 0xE2, 0xFD, 0x13, 0xBC, 0x9A, 0x51, 0x7D, 0xF2, 0xE9, + 0x17, 0x8F, 0x33, 0x91, 0x28, 0xDB, 0xA1, 0x22, 0x5F, 0xB3, 0x14, 0x65, 0xE9, 0x57, 0x92, 0xAC, + 0x33, 0xC5, 0xF8, 0x3E, 0x37, 0x35, 0x00, 0x88, 0x1C, 0x96, 0x62, 0x97, 0x17, 0x09, 0xA5, 0x3F, + 0x42, 0x18, 0x98, 0xD5, 0x45, 0x96, 0x1D, 0x0A, 0x46, 0x2C, 0xB9, 0x94, 0xFE, 0x35, 0xF7, 0x00, + 0x00, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xD0, 0xA2, 0xD9, 0x95, 0x6B, 0xDC, 0x69, 0xEA, 0x2D, + 0x52, 0xB0, 0x43, 0xA8, 0x7C, 0xA9, 0x32, 0x0E, 0x01, 0x00, 0x09, }; // This function can be used to generate textual representation of vector<uint8_t> like you see above. @@ -174,6 +175,7 @@ std::string FormatBytesFromBuffer(std::vector<uint8_t> const & buffer) } auto const kDefaultLang = StringUtf8Multilang::kDefaultCode; +auto const kEnLang = StringUtf8Multilang::kEnglishCode; auto const kRuLang = static_cast<int8_t>(8); kml::FileData GenerateKmlFileData() @@ -184,22 +186,29 @@ kml::FileData GenerateKmlFileData() result.m_categoryData.m_name[kRuLang] = "Тестовая категория"; result.m_categoryData.m_description[kDefaultLang] = "Test description"; result.m_categoryData.m_description[kRuLang] = "Тестовое описание"; + result.m_categoryData.m_annotation[kDefaultLang] = "Test annotation"; + result.m_categoryData.m_annotation[kEnLang] = "Test annotation"; + result.m_categoryData.m_imageUrl = "https://localhost/123.png"; result.m_categoryData.m_visible = true; result.m_categoryData.m_authorName = "Maps.Me"; result.m_categoryData.m_authorId = "12345"; + result.m_categoryData.m_rating = 8.9; + result.m_categoryData.m_reviewsNumber = 567; 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_toponyms = {"Georgia", "Gudauri"}; + result.m_categoryData.m_cities = {m2::PointD(35.0, 56.0), m2::PointD(20.0, 68.0)}; result.m_categoryData.m_languageCodes = {1, 2, 8}; + result.m_categoryData.m_properties = {{"property1", "value1"}, {"property2", "value2"}}; kml::BookmarkData bookmarkData; - bookmarkData.m_id = 10; bookmarkData.m_name[kDefaultLang] = "Test bookmark"; bookmarkData.m_name[kRuLang] = "Тестовая метка"; bookmarkData.m_description[kDefaultLang] = "Test bookmark description"; bookmarkData.m_description[kRuLang] = "Тестовое описание метки"; - bookmarkData.m_types = {8, 13, 34, 565}; + bookmarkData.m_featureTypes = {8, 13, 34, 565}; + bookmarkData.m_featureName[kDefaultLang] = "Гостиница Москва"; + bookmarkData.m_featureName[kEnLang] = "Moscow Inn Hotel"; bookmarkData.m_color = {kml::PredefinedColor::Blue, 0}; bookmarkData.m_icon = kml::BookmarkIcon::None; bookmarkData.m_viewportScale = 15; @@ -209,7 +218,6 @@ kml::FileData GenerateKmlFileData() result.m_bookmarksData.emplace_back(std::move(bookmarkData)); kml::TrackData trackData; - trackData.m_id = 121; trackData.m_localId = 0; trackData.m_name[kDefaultLang] = "Test track"; trackData.m_name[kRuLang] = "Тестовый трек"; @@ -292,26 +300,37 @@ char const * kGeneratedKml = " <mwm:name>\n" " <mwm:lang code=\"ru\">Тестовая категория</mwm:lang>\n" " </mwm:name>\n" + " <mwm:annotation>\n" + " <mwm:lang code=\"en\">Test annotation</mwm:lang>\n" + " <mwm:lang code=\"default\">Test annotation</mwm:lang>\n" + " </mwm:annotation>\n" " <mwm:description>\n" " <mwm:lang code=\"ru\">Тестовое описание</mwm:lang>\n" " </mwm:description>\n" + " <mwm:imageUrl>https://localhost/123.png</mwm:imageUrl>\n" " <mwm:author id=\"12345\">Maps.Me</mwm:author>\n" " <mwm:lastModified>1970-01-01T00:16:40Z</mwm:lastModified>\n" + " <mwm:rating>8.9</mwm:rating>\n" + " <mwm:reviewsNumber>567</mwm:reviewsNumber>\n" " <mwm:accessRules>Public</mwm:accessRules>\n" " <mwm:tags>\n" " <mwm:value>mountains</mwm:value>\n" " <mwm:value>ski</mwm:value>\n" " <mwm:value>snowboard</mwm:value>\n" " </mwm:tags>\n" - " <mwm:toponyms>\n" - " <mwm:value>Georgia</mwm:value>\n" - " <mwm:value>Gudauri</mwm:value>\n" - " </mwm:toponyms>\n" + " <mwm:cities>\n" + " <mwm:value>35,48.757959</mwm:value>\n" + " <mwm:value>20,56.056771</mwm:value>\n" + " </mwm:cities>\n" " <mwm:languageCodes>\n" " <mwm:value>en</mwm:value>\n" " <mwm:value>ja</mwm:value>\n" " <mwm:value>ru</mwm:value>\n" " </mwm:languageCodes>\n" + " <mwm:properties>\n" + " <mwm:value key=\"property1\">value1</mwm:value>\n" + " <mwm:value key=\"property2\">value2</mwm:value>\n" + " </mwm:properties>\n" " </ExtendedData>\n" " <Placemark>\n" " <name>Test bookmark</name>\n" @@ -326,12 +345,16 @@ char const * kGeneratedKml = " <mwm:description>\n" " <mwm:lang code=\"ru\">Тестовое описание метки</mwm:lang>\n" " </mwm:description>\n" - " <mwm:types>\n" + " <mwm:featureTypes>\n" " <mwm:value>8</mwm:value>\n" " <mwm:value>13</mwm:value>\n" " <mwm:value>34</mwm:value>\n" " <mwm:value>565</mwm:value>\n" - " </mwm:types>\n" + " </mwm:featureTypes>\n" + " <mwm:featureName>\n" + " <mwm:lang code=\"en\">Moscow Inn Hotel</mwm:lang>\n" + " <mwm:lang code=\"default\">Гостиница Москва</mwm:lang>\n" + " </mwm:featureName>\n" " <mwm:scale>15</mwm:scale>\n" " <mwm:boundTracks>\n" " <mwm:value>0</mwm:value>\n" @@ -593,6 +616,15 @@ UNIT_TEST(Kml_Serialization_Text_File) TEST(false, ("Exception raised", exc.what())); } +// TODO: uncomment to output KML to the log. +// std::string buffer; +// { +// kml::SerializerKml ser(data); +// MemWriter<decltype(buffer)> sink(buffer); +// ser.Serialize(sink); +// } +// LOG(LINFO, (buffer)); + kml::FileData dataFromFile; try { @@ -604,6 +636,7 @@ UNIT_TEST(Kml_Serialization_Text_File) { TEST(false, ("Exception raised", exc.what())); } + TEST_EQUAL(dataFromFile, data, ()); kml::FileData dataFromMemory; { @@ -611,6 +644,5 @@ UNIT_TEST(Kml_Serialization_Text_File) MemReader reader(kGeneratedKml, strlen(kGeneratedKml)); des.Deserialize(reader); } - - TEST_EQUAL(dataFromFile, dataFromMemory, ()); + TEST_EQUAL(dataFromMemory, data, ()); } diff --git a/kml/serdes.cpp b/kml/serdes.cpp index 99f9c33667..82a37367cd 100644 --- a/kml/serdes.cpp +++ b/kml/serdes.cpp @@ -130,6 +130,9 @@ uint32_t ToRGBA(Channel red, Channel green, Channel blue, Channel alpha) void SaveStringWithCDATA(KmlWriter::WriterWrapper & writer, std::string const & s) { + if (s.empty()) + return; + // According to kml/xml spec, we need to escape special symbols with CDATA. if (s.find_first_of("<&") != std::string::npos) writer << "<![CDATA[" << s << "]]>"; @@ -177,7 +180,7 @@ void SaveLocalizableString(KmlWriter::WriterWrapper & writer, LocalizableString writer << offsetStr << "<mwm:" << tagName << ">\n"; for (auto const & s : str) { - if (s.first == kDefaultLang) + if (s.first == kDefaultLang && (tagName == "name" || tagName == "description")) continue; writer << offsetStr << kIndent2 << "<mwm:lang code=\"" @@ -205,26 +208,84 @@ void SaveStringsArray(KmlWriter::WriterWrapper & writer, writer << offsetStr << "</mwm:" << tagName << ">\n"; } +void SavePointsArray(KmlWriter::WriterWrapper & writer, + std::vector<m2::PointD> const & pointsArray, + std::string const & tagName, std::string const & offsetStr) +{ + if (pointsArray.empty()) + return; + + writer << offsetStr << "<mwm:" << tagName << ">\n"; + for (auto const & p : pointsArray) + { + writer << offsetStr << kIndent2 << "<mwm:value>"; + writer << PointToString(p); + writer << "</mwm:value>\n"; + } + writer << offsetStr << "</mwm:" << tagName << ">\n"; +} + +void SaveStringsMap(KmlWriter::WriterWrapper & writer, + std::map<std::string, std::string> const & stringsMap, + std::string const & tagName, std::string const & offsetStr) +{ + if (stringsMap.empty()) + return; + + writer << offsetStr << "<mwm:" << tagName << ">\n"; + for (auto const & p : stringsMap) + { + writer << offsetStr << kIndent2 << "<mwm:value key=\"" << p.first << "\">"; + SaveStringWithCDATA(writer, p.second); + writer << "</mwm:value>\n"; + } + writer << offsetStr << "</mwm:" << tagName << ">\n"; +} + void SaveCategoryExtendedData(KmlWriter::WriterWrapper & writer, CategoryData const & categoryData) { writer << kIndent2 << kExtendedDataHeader; + SaveLocalizableString(writer, categoryData.m_name, "name", kIndent4); + SaveLocalizableString(writer, categoryData.m_annotation, "annotation", kIndent4); SaveLocalizableString(writer, categoryData.m_description, "description", kIndent4); + + if (!categoryData.m_imageUrl.empty()) + writer << kIndent4 << "<mwm:imageUrl>" << categoryData.m_imageUrl << "</mwm:imageUrl>\n"; + if (!categoryData.m_authorId.empty() || !categoryData.m_authorName.empty()) { writer << kIndent4 << "<mwm:author id=\"" << categoryData.m_authorId << "\">"; SaveStringWithCDATA(writer, categoryData.m_authorName); writer << "</mwm:author>\n"; } + if (categoryData.m_lastModified != Timestamp()) { writer << kIndent4 << "<mwm:lastModified>" << TimestampToString(categoryData.m_lastModified) << "</mwm:lastModified>\n"; } + + double constexpr kEps = 1e-5; + if (fabs(categoryData.m_rating) > kEps) + { + writer << kIndent4 << "<mwm:rating>" << strings::to_string(categoryData.m_rating) + << "</mwm:rating>\n"; + } + + if (categoryData.m_reviewsNumber > 0) + { + writer << kIndent4 << "<mwm:reviewsNumber>" + << strings::to_string(categoryData.m_reviewsNumber) << "</mwm:reviewsNumber>\n"; + } + writer << kIndent4 << "<mwm:accessRules>" << DebugPrint(categoryData.m_accessRules) << "</mwm:accessRules>\n"; + SaveStringsArray(writer, categoryData.m_tags, "tags", kIndent4); - SaveStringsArray(writer, categoryData.m_toponyms, "toponyms", kIndent4); + + SavePointsArray(writer, categoryData.m_cities, "cities", kIndent4); + std::vector<std::string> languageCodes; languageCodes.reserve(categoryData.m_languageCodes.size()); for (auto const & lang : categoryData.m_languageCodes) @@ -234,6 +295,9 @@ void SaveCategoryExtendedData(KmlWriter::WriterWrapper & writer, CategoryData co languageCodes.push_back(std::move(str)); } SaveStringsArray(writer, languageCodes, "languageCodes", kIndent4); + + SaveStringsMap(writer, categoryData.m_properties, "properties", kIndent4); + writer << kIndent2 << kExtendedDataFooter; } @@ -261,8 +325,9 @@ void SaveCategoryData(KmlWriter::WriterWrapper & writer, CategoryData const & ca void SaveBookmarkExtendedData(KmlWriter::WriterWrapper & writer, BookmarkData const & bookmarkData) { if (bookmarkData.m_name.size() < 2 && bookmarkData.m_description.size() < 2 && - bookmarkData.m_viewportScale == 0 && bookmarkData.m_icon == BookmarkIcon::None && - bookmarkData.m_types.empty() && bookmarkData.m_boundTracks.empty()) + bookmarkData.m_featureName.empty() && bookmarkData.m_viewportScale == 0 && + bookmarkData.m_icon == BookmarkIcon::None && bookmarkData.m_featureTypes.empty() && + bookmarkData.m_boundTracks.empty()) { return; } @@ -272,10 +337,13 @@ void SaveBookmarkExtendedData(KmlWriter::WriterWrapper & writer, BookmarkData co SaveLocalizableString(writer, bookmarkData.m_description, "description", kIndent6); std::vector<std::string> types; - types.reserve(bookmarkData.m_types.size()); - for (auto const & t : bookmarkData.m_types) + types.reserve(bookmarkData.m_featureTypes.size()); + for (auto const & t : bookmarkData.m_featureTypes) types.push_back(strings::to_string(t)); - SaveStringsArray(writer, types, "types", kIndent6); + SaveStringsArray(writer, types, "featureTypes", kIndent6); + + if (!bookmarkData.m_featureName.empty()) + SaveLocalizableString(writer, bookmarkData.m_featureName, "featureName", kIndent6); if (bookmarkData.m_viewportScale != 0) { @@ -442,7 +510,8 @@ void KmlParser::ResetPoint() m_mapStyleId.clear(); m_styleUrlKey.clear(); - m_types.clear(); + m_featureTypes.clear(); + m_featureName.clear(); m_boundTracks.clear(); m_localId = 0; m_trackLayers.clear(); @@ -571,6 +640,8 @@ void KmlParser::AddAttr(std::string const & attr, std::string const & value) m_attrCode = StringUtf8Multilang::GetLangIndex(value); else if (attrInLowerCase == "id") m_attrId = value; + else if (attrInLowerCase == "key") + m_attrKey = value; } bool KmlParser::IsValidAttribute(std::string const & type, std::string const & value, @@ -596,15 +667,16 @@ void KmlParser::Pop(std::string const & tag) if (GEOMETRY_TYPE_POINT == m_geometryType) { BookmarkData data; - data.m_name = m_name; - data.m_description = m_description; + data.m_name = std::move(m_name); + data.m_description = std::move(m_description); data.m_color.m_predefinedColor = m_predefinedColor; data.m_color.m_rgba = m_color; data.m_icon = m_icon; data.m_viewportScale = m_viewportScale; data.m_timestamp = m_timestamp; data.m_point = m_org; - data.m_types = std::move(m_types); + data.m_featureTypes = std::move(m_featureTypes); + data.m_featureName = std::move(m_featureName); data.m_boundTracks = std::move(m_boundTracks); m_data.m_bookmarksData.push_back(std::move(data)); } @@ -612,8 +684,8 @@ void KmlParser::Pop(std::string const & tag) { TrackData data; data.m_localId = m_localId; - data.m_name = m_name; - data.m_description = m_description; + data.m_name = std::move(m_name); + data.m_description = std::move(m_description); data.m_layers = std::move(m_trackLayers); data.m_timestamp = m_timestamp; data.m_points = m_points; @@ -688,13 +760,29 @@ void KmlParser::CharData(std::string value) if (value == "Public") m_data.m_categoryData.m_accessRules = AccessRules::Public; } + else if (currTag == "mwm:imageUrl") + { + m_data.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; + } + else if (currTag == "mwm:reviewsNumber") + { + if (!strings::to_uint(value, m_data.m_categoryData.m_reviewsNumber)) + m_data.m_categoryData.m_reviewsNumber = 0; + } } else if (pppTag == kDocument && ppTag == kExtendedData && currTag == "mwm:lang") { - if (prevTag == "mwm:name" && m_attrCode > 0) + if (prevTag == "mwm:name" && m_attrCode >= 0) m_data.m_categoryData.m_name[m_attrCode] = value; - else if (prevTag == "mwm:description" && m_attrCode > 0) + else if (prevTag == "mwm:description" && m_attrCode >= 0) m_data.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_attrCode = StringUtf8Multilang::kUnsupportedLanguageCode; } else if (pppTag == kDocument && ppTag == kExtendedData && currTag == "mwm:value") @@ -703,9 +791,11 @@ void KmlParser::CharData(std::string value) { m_data.m_categoryData.m_tags.push_back(value); } - else if (prevTag == "mwm:toponyms") + else if (prevTag == "mwm:cities") { - m_data.m_categoryData.m_toponyms.push_back(value); + m2::PointD pt; + if (ParsePoint(value, ", \n\r\t", pt)) + m_data.m_categoryData.m_cities.push_back(pt); } else if (prevTag == "mwm:languageCodes") { @@ -713,6 +803,11 @@ void KmlParser::CharData(std::string value) if (lang != StringUtf8Multilang::kUnsupportedLanguageCode) m_data.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_attrKey.clear(); + } } else if (prevTag == kPlacemark) { @@ -845,17 +940,19 @@ void KmlParser::CharData(std::string value) { if (currTag == "mwm:lang") { - if (prevTag == "mwm:name" && m_attrCode > 0) + if (prevTag == "mwm:name" && m_attrCode >= 0) m_name[m_attrCode] = value; - else if (prevTag == "mwm:description" && m_attrCode > 0) + else if (prevTag == "mwm:description" && m_attrCode >= 0) m_description[m_attrCode] = value; + else if (prevTag == "mwm:featureName" && m_attrCode >= 0) + m_featureName[m_attrCode] = value; m_attrCode = StringUtf8Multilang::kUnsupportedLanguageCode; } else if (currTag == "mwm:value") { uint32_t i; - if (prevTag == "mwm:types" && strings::to_uint(value, i)) - m_types.push_back(i); + if (prevTag == "mwm:featureTypes" && strings::to_uint(value, i)) + m_featureTypes.push_back(i); else if (prevTag == "mwm:boundTracks" && strings::to_uint(value, i)) m_boundTracks.push_back(static_cast<LocalId>(i)); } diff --git a/kml/serdes.hpp b/kml/serdes.hpp index 7aea2c7de4..cbe4a63f64 100644 --- a/kml/serdes.hpp +++ b/kml/serdes.hpp @@ -102,6 +102,7 @@ private: int8_t m_attrCode; std::string m_attrId; + std::string m_attrKey; LocalizableString m_name; LocalizableString m_description; @@ -109,7 +110,8 @@ private: Timestamp m_timestamp; m2::PointD m_org; uint8_t m_viewportScale; - std::vector<uint32_t> m_types; + std::vector<uint32_t> m_featureTypes; + LocalizableString m_featureName; std::vector<LocalId> m_boundTracks; LocalId m_localId; BookmarkIcon m_icon; diff --git a/kml/type_utils.hpp b/kml/type_utils.hpp index 3388388779..85782b16e8 100644 --- a/kml/type_utils.hpp +++ b/kml/type_utils.hpp @@ -16,6 +16,7 @@ using Timestamp = std::chrono::time_point<std::chrono::system_clock>; using LocalizableString = std::unordered_map<int8_t, std::string>; using LocalizableStringSubIndex = std::map<int8_t, uint32_t>; using LocalizableStringIndex = std::vector<LocalizableStringSubIndex>; +using Properties = std::map<std::string, std::string>; using CategoryId = uint64_t; using BookmarkId = uint64_t; using TrackId = uint64_t; @@ -60,6 +61,10 @@ inline bool IsEqual(Timestamp const & ts1, Timestamp const & ts2) uint32_t constexpr kEmptyStringId = 0; double constexpr kMinLineWidth = 0.0; double constexpr kMaxLineWidth = 100.0; +uint32_t constexpr kLineWidthBits = 30; +double constexpr kMinRating = 0.0; +double constexpr kMaxRating = 10.0; +uint32_t constexpr kRatingBits = 30; #define DECLARE_COLLECTABLE(IndexType, ...) \ IndexType m_collectionIndex; \ diff --git a/kml/types.hpp b/kml/types.hpp index 5b6b4711a8..2389b08c3c 100644 --- a/kml/types.hpp +++ b/kml/types.hpp @@ -102,7 +102,8 @@ struct BookmarkData DECLARE_VISITOR_AND_DEBUG_PRINT(BookmarkData, visitor(m_id, "id"), visitor(m_name, "name"), visitor(m_description, "description"), - visitor(m_types, "types"), + visitor(m_featureTypes, "featureTypes"), + visitor(m_featureName, "featureName"), visitor(m_color, "color"), visitor(m_icon, "icon"), visitor(m_viewportScale, "viewportScale"), @@ -111,7 +112,7 @@ struct BookmarkData visitor(m_boundTracks, "boundTracks"), VISITOR_COLLECTABLE) - DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description) + DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description, m_featureName) bool operator==(BookmarkData const & data) const { @@ -121,7 +122,9 @@ struct BookmarkData 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_types == data.m_types && + m_point.EqualDxDy(data.m_point, kEps) && + m_featureTypes == data.m_featureTypes && + m_featureName == data.m_featureName && m_boundTracks == data.m_boundTracks; } @@ -133,8 +136,10 @@ struct BookmarkData LocalizableString m_name; // Bookmark's description. LocalizableString m_description; - // Bookmark's types. - std::vector<uint32_t> m_types; + // Bound feature's types. + std::vector<uint32_t> m_featureTypes; + // Bound feature's name. + LocalizableString m_featureName; // Bookmark's color. ColorData m_color; // Bookmark's icon. @@ -210,27 +215,36 @@ struct CategoryData { DECLARE_VISITOR_AND_DEBUG_PRINT(CategoryData, 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_toponyms, "toponyms"), + visitor(m_cities, "cities"), visitor(m_languageCodes, "languageCodes"), + visitor(m_properties, "properties"), VISITOR_COLLECTABLE) - DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description, - m_authorName, m_authorId, m_tags, m_toponyms) + DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_annotation, m_description, + m_imageUrl, m_authorName, m_authorId, m_tags, m_properties) bool operator==(CategoryData const & data) const { - return m_id == data.m_id && m_name == data.m_name && m_description == data.m_description && + 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 && - m_toponyms == data.m_toponyms && m_languageCodes == data.m_languageCodes; + IsEqual(m_cities, data.m_cities) && m_languageCodes == data.m_languageCodes && + m_properties == data.m_properties; } bool operator!=(CategoryData const & data) const { return !operator==(data); } @@ -239,6 +253,10 @@ struct CategoryData CategoryId m_id = kInvalidCategoryId; // 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. @@ -249,14 +267,20 @@ struct CategoryData 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::Private; // Collection of tags. std::vector<std::string> m_tags; - // Collection of toponyms. - std::vector<std::string> m_toponyms; + // Collection of cities coordinates. + std::vector<m2::PointD> m_cities; // Language codes. std::vector<int8_t> m_languageCodes; + // Key-value properties. + Properties m_properties; }; struct FileData diff --git a/kml/visitors.hpp b/kml/visitors.hpp index 76ea7a6ec5..28ecb51bb6 100644 --- a/kml/visitors.hpp +++ b/kml/visitors.hpp @@ -131,6 +131,24 @@ public: Collect(index, args...); } + template <typename... OtherStrings> + void Collect(LocalizableStringIndex & index, Properties const & properties, + OtherStrings const & ... args) + { + index.emplace_back(LocalizableStringSubIndex()); + auto constexpr kMaxSize = std::numeric_limits<int8_t>::max() - 1; + int8_t counter = 0; + for (auto const & p : properties) + { + if (counter >= kMaxSize) + break; + CollectString(index.back(), counter++, p.first); + CollectString(index.back(), counter++, p.second); + } + + Collect(index, args...); + } + template <typename...> void Collect(LocalizableStringIndex & index) {} @@ -201,6 +219,18 @@ public: WriteVarUint(m_sink, ToSecondsSinceEpoch(t)); } + void operator()(double d, char const * /* name */ = nullptr) + { + uint64_t const encoded = DoubleToUint32(d, kMinRating, kMaxRating, kRatingBits); + WriteVarUint(m_sink, encoded); + } + + void operator()(m2::PointD const & pt, char const * /* name */ = nullptr) + { + uint64_t const encoded = bits::ZigZagEncode(PointToInt64(pt, POINT_COORD_BITS)); + WriteVarUint(m_sink, encoded); + } + template <typename T> void operator()(std::vector<T> const & vs, char const * /* name */ = nullptr) { @@ -227,6 +257,7 @@ public: SKIP_VISITING(LocalizableString const &) SKIP_VISITING(std::string const &) SKIP_VISITING(std::vector<std::string> const &) + SKIP_VISITING(Properties const &) SKIP_VISITING(std::vector<BookmarkData> const &) SKIP_VISITING(std::vector<TrackData> const &) @@ -260,7 +291,7 @@ public: void operator()(double d, char const * /* name */ = nullptr) { - uint64_t const encoded = DoubleToUint32(d, kMinLineWidth, kMaxLineWidth, 30 /* coordBits */); + uint64_t const encoded = DoubleToUint32(d, kMinLineWidth, kMaxLineWidth, kLineWidthBits); WriteVarUint(m_sink, encoded); } @@ -305,6 +336,7 @@ public: SKIP_VISITING(LocalizableString const &) SKIP_VISITING(std::string const &) SKIP_VISITING(std::vector<std::string> const &) + SKIP_VISITING(Properties const &) private: Sink & m_sink; @@ -357,6 +389,18 @@ public: t = FromSecondsSinceEpoch(v); } + void operator()(double & d, char const * /* name */ = nullptr) + { + auto const v = ReadVarUint<uint32_t, Source>(m_source); + d = Uint32ToDouble(v, kMinRating, kMaxRating, kRatingBits); + } + + void operator()(m2::PointD & pt, char const * /* name */ = nullptr) + { + auto const v = ReadVarUint<uint64_t, Source>(m_source); + pt = Int64ToPoint(bits::ZigZagDecode(v), POINT_COORD_BITS); + } + template <typename T> void operator()(std::vector<T> & vs, char const * /* name */ = nullptr) { @@ -387,6 +431,7 @@ public: SKIP_VISITING(LocalizableString &) SKIP_VISITING(std::string &) SKIP_VISITING(std::vector<std::string> &) + SKIP_VISITING(Properties &) SKIP_VISITING(std::vector<BookmarkData> &) SKIP_VISITING(std::vector<TrackData> &) @@ -421,7 +466,7 @@ public: void operator()(double & d, char const * /* name */ = nullptr) { auto const v = ReadVarUint<uint32_t, Source>(m_source); - d = Uint32ToDouble(v, kMinLineWidth, kMaxLineWidth, 30 /* coordBits */); + d = Uint32ToDouble(v, kMinLineWidth, kMaxLineWidth, kLineWidthBits); } void operator()(Timestamp & t, char const * /* name */ = nullptr) @@ -475,6 +520,7 @@ public: SKIP_VISITING(LocalizableString &) SKIP_VISITING(std::string &) SKIP_VISITING(std::vector<std::string> &) + SKIP_VISITING(Properties &) private: Source & m_source; @@ -497,7 +543,7 @@ public: auto subIndex = index[m_counter]; for (auto const & p : subIndex) - str[p.first] = ExtractString(subIndex, p.second); + str[p.first] = ExtractString(p.second); m_counter++; Collect(index, args...); @@ -512,7 +558,7 @@ public: auto subIndex = index[m_counter]; if (!subIndex.empty()) - str = ExtractString(subIndex, subIndex.begin()->second); + str = ExtractString(subIndex.begin()->second); else str = {}; @@ -521,7 +567,8 @@ public: } template <typename... OtherStrings> - void Collect(LocalizableStringIndex & index, std::vector<std::string> & stringsArray, + void Collect(LocalizableStringIndex & index, + std::vector<std::string> & stringsArray, OtherStrings & ... args) { if (!SwitchSubIndexIfNeeded(index)) @@ -530,7 +577,26 @@ public: auto subIndex = index[m_counter]; stringsArray.reserve(subIndex.size()); for (auto const & p : subIndex) - stringsArray.emplace_back(ExtractString(subIndex, p.second)); + stringsArray.emplace_back(ExtractString(p.second)); + + m_counter++; + Collect(index, args...); + } + + template <typename... OtherStrings> + void Collect(LocalizableStringIndex & index, Properties & properties, + OtherStrings & ... args) + { + if (!SwitchSubIndexIfNeeded(index)) + return; + + auto subIndex = index[m_counter]; + auto const sz = static_cast<int8_t>(subIndex.size() / 2); + for (int8_t i = 0; i < sz; i++) + { + properties.insert(std::make_pair(ExtractString(subIndex[2 * i]), + ExtractString(subIndex[2 * i + 1]))); + } m_counter++; Collect(index, args...); @@ -550,8 +616,7 @@ private: return m_counter < index.size(); } - std::string ExtractString(LocalizableStringSubIndex const & subIndex, - uint32_t stringIndex) const + std::string ExtractString(uint32_t stringIndex) const { auto const stringsCount = m_textStorage.GetNumStrings(); if (stringIndex >= stringsCount) |