Added maps.me extension to KML format

This commit is contained in:
r.kuznetsov 2018-03-15 16:33:34 +03:00 committed by Daria Volvenkova
parent da74ce46ad
commit ea9a076055
3 changed files with 469 additions and 110 deletions

View file

@ -124,40 +124,40 @@ char const * kTextKml =
std::vector<uint8_t> const kBinKml = {
0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00,
0x00, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00,
0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2,
0xAB, 0xEB, 0x96, 0xE2, 0xFE, 0x83, 0xD4, 0x11, 0x00, 0x02, 0x01, 0x00, 0x03, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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,
0xAB, 0xEB, 0x96, 0xE2, 0xFE, 0x83, 0xD4, 0x11, 0x00, 0x02, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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,
};
// This function can be used to generate textual representation of vector<uint8_t> like you see above.
@ -174,12 +174,15 @@ std::string FormatBytesFromBuffer(std::vector<uint8_t> const & buffer)
}
auto const kDefaultLang = StringUtf8Multilang::kDefaultCode;
auto const kRuLang = static_cast<int8_t>(8);
kml::CategoryData GenerateCategoryData()
{
kml::CategoryData categoryData;
categoryData.m_name[kDefaultLang] = "Test category";
categoryData.m_name[kRuLang] = "Тестовая категория";
categoryData.m_description[kDefaultLang] = "Test description";
categoryData.m_description[kRuLang] = "Тестовое описание";
categoryData.m_visible = true;
categoryData.m_authorName = "Maps.Me";
categoryData.m_authorId = "12345";
@ -187,12 +190,14 @@ kml::CategoryData GenerateCategoryData()
categoryData.m_accessRules = kml::AccessRules::Public;
categoryData.m_tags = {"mountains", "ski", "snowboard"};
categoryData.m_toponyms = {"Georgia", "Gudauri"};
categoryData.m_languageCodes = {0, 2, 5};
categoryData.m_languageCodes = {1, 2, 8};
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_color = {kml::PredefinedColor::Blue, 0};
bookmarkData.m_icon = kml::BookmarkIcon::None;
@ -206,7 +211,9 @@ kml::CategoryData GenerateCategoryData()
trackData.m_id = 121;
trackData.m_localId = 0;
trackData.m_name[kDefaultLang] = "Test track";
trackData.m_name[kRuLang] = "Тестовый трек";
trackData.m_description[kDefaultLang] = "Test track description";
trackData.m_description[kRuLang] = "Тестовое описание трека";
trackData.m_layers = {{6.0, {kml::PredefinedColor::None, 0xff0000ff}},
{7.0, {kml::PredefinedColor::None, 0x00ff00ff}}};
trackData.m_timestamp = std::chrono::system_clock::from_time_t(900);
@ -280,14 +287,54 @@ char const * kGeneratedKml =
" <name>Test category</name>\n"
" <description>Test description</description>\n"
" <visibility>1</visibility>\n"
" <ExtendedData xmlns:mwm=\"https://maps.me\">\n"
" <mwm:name>\n"
" <mwm:lang code=\"ru\">Тестовая категория</mwm:lang>\n"
" </mwm:name>\n"
" <mwm:description>\n"
" <mwm:lang code=\"ru\">Тестовое описание</mwm:lang>\n"
" </mwm:description>\n"
" <mwm:author id=\"12345\">Maps.Me</mwm:author>\n"
" <mwm:lastModified>1970-01-01T00:16:40Z</mwm:lastModified>\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:languageCodes>\n"
" <mwm:value>en</mwm:value>\n"
" <mwm:value>ja</mwm:value>\n"
" <mwm:value>ru</mwm:value>\n"
" </mwm:languageCodes>\n"
" </ExtendedData>\n"
" <Placemark>\n"
" <name>Test bookmark</name>\n"
" <description>Test bookmark description</description>\n"
" <TimeStamp><when>1970-01-01T00:13:20Z</when></TimeStamp>\n"
" <styleUrl>#placemark-blue</styleUrl>\n"
" <Point><coordinates>45.9242,49.326859</coordinates></Point>\n"
" <ExtendedData xmlns:mwm=\"http://mapswith.me\">\n"
" <ExtendedData xmlns:mwm=\"https://maps.me\">\n"
" <mwm:name>\n"
" <mwm:lang code=\"ru\">Тестовая метка</mwm:lang>\n"
" </mwm:name>\n"
" <mwm:description>\n"
" <mwm:lang code=\"ru\">Тестовое описание метки</mwm:lang>\n"
" </mwm:description>\n"
" <mwm:types>\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:scale>15</mwm:scale>\n"
" <mwm:boundTracks>\n"
" <mwm:value>0</mwm:value>\n"
" </mwm:boundTracks>\n"
" </ExtendedData>\n"
" </Placemark>\n"
" <Placemark>\n"
@ -297,7 +344,23 @@ char const * kGeneratedKml =
" <color>FF0000FF</color>\n"
" <width>6</width>\n"
" </LineStyle></Style>\n"
" <TimeStamp><when>1970-01-01T00:15:00Z</when></TimeStamp>\n"
" <LineString><coordinates>45.9242,49.326859 45.2244,48.941288 45.1964,49.401948</coordinates></LineString>\n"
" <ExtendedData xmlns:mwm=\"https://maps.me\">\n"
" <mwm:name>\n"
" <mwm:lang code=\"ru\">Тестовый трек</mwm:lang>\n"
" </mwm:name>\n"
" <mwm:description>\n"
" <mwm:lang code=\"ru\">Тестовое описание трека</mwm:lang>\n"
" </mwm:description>\n"
" <mwm:localId>0</mwm:localId>\n"
" <mwm:additionalStyle>\n"
" <mwm:additionalLineStyle>\n"
" <color>FF00FF00</color>\n"
" <width>7</width>\n"
" </mwm:additionalLineStyle>\n"
" </mwm:additionalStyle>\n"
" </ExtendedData>\n"
" </Placemark>\n"
"</Document>\n"
"</kml>";
@ -320,6 +383,15 @@ UNIT_TEST(Kml_Deserialization_Text_Bin_Memory)
TEST(false, ("Exception raised", exc.what()));
}
// TODO: uncomment to output bytes to the log.
// std::vector<uint8_t> buffer;
// {
// kml::binary::SerializerKml ser(dataFromText);
// MemWriter<decltype(buffer)> sink(buffer);
// ser.Serialize(sink);
// }
// LOG(LINFO, (FormatBytesFromBuffer(buffer)));
kml::CategoryData dataFromBin;
try
{

View file

@ -24,6 +24,7 @@ std::string const kDocument = "Document";
std::string const kStyleMap = "StyleMap";
std::string const kStyleUrl = "styleUrl";
std::string const kPair = "Pair";
std::string const kExtendedData = "ExtendedData";
std::string const kKmlHeader =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
@ -34,8 +35,31 @@ std::string const kKmlFooter =
"</Document>\n"
"</kml>\n";
std::string const kExtendedDataHeader =
"<ExtendedData xmlns:mwm=\"https://maps.me\">\n";
std::string const kExtendedDataFooter =
"</ExtendedData>\n";
auto const kDefaultLang = StringUtf8Multilang::kDefaultCode;
auto const kDefaultTrackWidth = 5.0;
std::string Indent(size_t count)
{
static std::string const kIndent = " ";
std::ostringstream indent;
for (size_t i = 0; i < count; ++i)
indent << kIndent;
return indent.str();
}
static std::string const kIndent2 = Indent(2);
static std::string const kIndent4 = Indent(4);
static std::string const kIndent6 = Indent(6);
static std::string const kIndent8 = Indent(8);
static std::string const kIndent10 = Indent(10);
std::string PointToString(m2::PointD const & org)
{
double const lon = MercatorBounds::XToLon(org.x);
@ -118,13 +142,13 @@ void SaveStyle(KmlWriter::WriterWrapper & writer, std::string const & style)
if (style.empty())
return;
writer << " <Style id=\"" << style << "\">\n"
<< " <IconStyle>\n"
<< " <Icon>\n"
<< " <href>http://mapswith.me/placemarks/" << style << ".png</href>\n"
<< " </Icon>\n"
<< " </IconStyle>\n"
<< " </Style>\n";
writer << kIndent2 << "<Style id=\"" << style << "\">\n"
<< kIndent4 << "<IconStyle>\n"
<< kIndent6 << "<Icon>\n"
<< kIndent8 << "<href>http://mapswith.me/placemarks/" << style << ".png</href>\n"
<< kIndent6 << "</Icon>\n"
<< kIndent4 << "</IconStyle>\n"
<< kIndent2 << "</Style>\n";
}
void SaveColorToABGR(KmlWriter::WriterWrapper & writer, uint32_t rgba)
@ -135,75 +159,211 @@ void SaveColorToABGR(KmlWriter::WriterWrapper & writer, uint32_t rgba)
<< NumToHex(static_cast<uint8_t>((rgba >> 24) & 0xFF));
}
std::string TimestampToString(Timestamp const & timestamp)
{
auto const ts = std::chrono::system_clock::to_time_t(timestamp);
std::string const strTimeStamp = my::TimestampToString(ts);
if (strTimeStamp.size() != 20)
MYTHROW(KmlWriter::WriteKmlException, ("We always generate fixed length UTC-format timestamp."));
return strTimeStamp;
}
void SaveLocalizableString(KmlWriter::WriterWrapper & writer, LocalizableString const & str,
std::string const & tagName, std::string const & offsetStr)
{
if (str.size() < 2)
return;
writer << offsetStr << "<mwm:" << tagName << ">\n";
for (auto const & s : str)
{
if (s.first == kDefaultLang)
continue;
writer << offsetStr << kIndent2 << "<mwm:lang code=\""
<< StringUtf8Multilang::GetLangByCode(s.first) << "\">";
SaveStringWithCDATA(writer, s.second);
writer << "</mwm:lang>\n";
}
writer << offsetStr << "</mwm:" << tagName << ">\n";
}
void SaveStringsArray(KmlWriter::WriterWrapper & writer,
std::vector<std::string> const & stringsArray,
std::string const & tagName, std::string const & offsetStr)
{
if (stringsArray.empty())
return;
writer << offsetStr << "<mwm:" << tagName << ">\n";
for (auto const & s : stringsArray)
{
writer << offsetStr << kIndent2 << "<mwm:value>";
SaveStringWithCDATA(writer, s);
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_description, "description", kIndent4);
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";
}
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);
std::vector<std::string> languageCodes;
languageCodes.reserve(categoryData.m_languageCodes.size());
for (auto const & lang : categoryData.m_languageCodes)
{
std::string str = StringUtf8Multilang::GetLangByCode(lang);
if (!str.empty())
languageCodes.push_back(std::move(str));
}
SaveStringsArray(writer, languageCodes, "languageCodes", kIndent4);
writer << kIndent2 << kExtendedDataFooter;
}
void SaveCategoryData(KmlWriter::WriterWrapper & writer, CategoryData const & categoryData)
{
for (uint8_t i = 0; i < my::Key(PredefinedColor::Count); ++i)
SaveStyle(writer, GetStyleForPredefinedColor(static_cast<PredefinedColor>(i)));
// Use CDATA if we have special symbols in the name.
writer << " <name>";
writer << kIndent2 << "<name>";
SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_name, kDefaultLang));
writer << "</name>\n";
if (!categoryData.m_description.empty())
{
writer << " <description>";
writer << kIndent2 << "<description>";
SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_description, kDefaultLang));
writer << "</description>\n";
}
writer << " <visibility>" << (categoryData.m_visible ? "1" : "0") <<"</visibility>\n";
writer << kIndent2 << "<visibility>" << (categoryData.m_visible ? "1" : "0") <<"</visibility>\n";
SaveCategoryExtendedData(writer, categoryData);
}
void SaveBoormarkData(KmlWriter::WriterWrapper & writer, BookmarkData const & bookmarkData)
void SaveBookmarkExtendedData(KmlWriter::WriterWrapper & writer, BookmarkData const & bookmarkData)
{
writer << " <Placemark>\n";
writer << " <name>";
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())
{
return;
}
writer << kIndent4 << kExtendedDataHeader;
SaveLocalizableString(writer, bookmarkData.m_name, "name", kIndent6);
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.push_back(strings::to_string(t));
SaveStringsArray(writer, types, "types", kIndent6);
if (bookmarkData.m_viewportScale != 0)
{
auto const scale = strings::to_string(static_cast<double>(bookmarkData.m_viewportScale));
writer << kIndent6 << "<mwm:scale>" << scale << "</mwm:scale>\n";
}
if (bookmarkData.m_icon != BookmarkIcon::None)
writer << kIndent6 << "<mwm:icon>" << DebugPrint(bookmarkData.m_icon) << "</mwm:icon>\n";
std::vector<std::string> boundTracks;
boundTracks.reserve(bookmarkData.m_boundTracks.size());
for (auto const & t : bookmarkData.m_boundTracks)
boundTracks.push_back(strings::to_string(static_cast<uint32_t>(t)));
SaveStringsArray(writer, boundTracks, "boundTracks", kIndent6);
writer << kIndent4 << kExtendedDataFooter;
}
void SaveBookmarkData(KmlWriter::WriterWrapper & writer, BookmarkData const & bookmarkData)
{
writer << kIndent2 << "<Placemark>\n";
writer << kIndent4 << "<name>";
SaveStringWithCDATA(writer, GetLocalizableString(bookmarkData.m_name, kDefaultLang));
writer << "</name>\n";
if (!bookmarkData.m_description.empty())
{
writer << " <description>";
writer << kIndent4 << "<description>";
SaveStringWithCDATA(writer, GetLocalizableString(bookmarkData.m_description, kDefaultLang));
writer << "</description>\n";
}
if (bookmarkData.m_timestamp != Timestamp())
{
auto const ts = std::chrono::system_clock::to_time_t(bookmarkData.m_timestamp);
std::string const strTimeStamp = my::TimestampToString(ts);
if (strTimeStamp.size() != 20)
MYTHROW(KmlWriter::WriteKmlException, ("We always generate fixed length UTC-format timestamp."));
writer << " <TimeStamp><when>" << strTimeStamp << "</when></TimeStamp>\n";
writer << kIndent4 << "<TimeStamp><when>" << TimestampToString(bookmarkData.m_timestamp)
<< "</when></TimeStamp>\n";
}
auto const style = GetStyleForPredefinedColor(bookmarkData.m_color.m_predefinedColor);
writer << " <styleUrl>#" << style << "</styleUrl>\n"
<< " <Point><coordinates>" << PointToString(bookmarkData.m_point)
writer << kIndent4 << "<styleUrl>#" << style << "</styleUrl>\n"
<< kIndent4 << "<Point><coordinates>" << PointToString(bookmarkData.m_point)
<< "</coordinates></Point>\n";
if (bookmarkData.m_viewportScale != 0)
{
auto const scale = strings::to_string(static_cast<double>(bookmarkData.m_viewportScale));
/// @todo Factor out to separate function to use for other custom params.
writer << " <ExtendedData xmlns:mwm=\"http://mapswith.me\">\n"
<< " <mwm:scale>" << scale << "</mwm:scale>\n"
<< " </ExtendedData>\n";
}
SaveBookmarkExtendedData(writer, bookmarkData);
writer << " </Placemark>\n";
writer << kIndent2 << "</Placemark>\n";
}
void SaveTrackLayer(KmlWriter::WriterWrapper & writer, TrackLayer const & layer,
std::string const & offsetStr)
{
writer << offsetStr << "<color>";
SaveColorToABGR(writer, layer.m_color.m_rgba);
writer << "</color>\n";
writer << offsetStr << "<width>" << strings::to_string(layer.m_lineWidth) << "</width>\n";
}
void SaveTrackExtendedData(KmlWriter::WriterWrapper & writer, TrackData const & trackData)
{
writer << kIndent4 << kExtendedDataHeader;
SaveLocalizableString(writer, trackData.m_name, "name", kIndent6);
SaveLocalizableString(writer, trackData.m_description, "description", kIndent6);
auto const localId = strings::to_string(static_cast<uint32_t>(trackData.m_localId));
writer << kIndent6 << "<mwm:localId>" << localId << "</mwm:localId>\n";
writer << kIndent6 << "<mwm:additionalStyle>\n";
for (size_t i = 1 /* since second layer */; i < trackData.m_layers.size(); ++i)
{
writer << kIndent8 << "<mwm:additionalLineStyle>\n";
SaveTrackLayer(writer, trackData.m_layers[i], kIndent10);
writer << kIndent8 << "</mwm:additionalLineStyle>\n";
}
writer << kIndent6 << "</mwm:additionalStyle>\n";
writer << kIndent4 << kExtendedDataFooter;
}
void SaveTrackData(KmlWriter::WriterWrapper & writer, TrackData const & trackData)
{
writer << " <Placemark>\n";
writer << " <name>";
writer << kIndent2 << "<Placemark>\n";
writer << kIndent4 << "<name>";
SaveStringWithCDATA(writer, GetLocalizableString(trackData.m_name, kDefaultLang));
writer << "</name>\n";
if (!trackData.m_description.empty())
{
writer << " <description>";
writer << kIndent4 << "<description>";
SaveStringWithCDATA(writer, GetLocalizableString(trackData.m_description, kDefaultLang));
writer << "</description>\n";
}
@ -212,24 +372,28 @@ void SaveTrackData(KmlWriter::WriterWrapper & writer, TrackData const & trackDat
MYTHROW(KmlWriter::WriteKmlException, ("Layers list is empty."));
auto const & layer = trackData.m_layers.front();
writer << " <Style><LineStyle>\n";
writer << " <color>";
SaveColorToABGR(writer, layer.m_color.m_rgba);
writer << "</color>\n";
writer << " <width>" << strings::to_string(layer.m_lineWidth) << "</width>\n";
writer << " </LineStyle></Style>\n";
writer << kIndent4 << "<Style><LineStyle>\n";
SaveTrackLayer(writer, layer, kIndent6);
writer << kIndent4 << "</LineStyle></Style>\n";
writer << " <LineString><coordinates>";
if (trackData.m_timestamp != Timestamp())
{
writer << kIndent4 << "<TimeStamp><when>" << TimestampToString(trackData.m_timestamp)
<< "</when></TimeStamp>\n";
}
writer << kIndent4 << "<LineString><coordinates>";
for (size_t i = 0; i < trackData.m_points.size(); ++i)
{
writer << PointToString(trackData.m_points[i]);
if (i + 1 != trackData.m_points.size())
writer << " ";
}
writer << "</coordinates></LineString>\n";
writer << " </Placemark>\n";
SaveTrackExtendedData(writer, trackData);
writer << kIndent2 << "</Placemark>\n";
}
} // namespace
@ -248,7 +412,7 @@ void KmlWriter::Write(CategoryData const & categoryData)
// Save bookmarks.
for (auto const & bookmarkData : categoryData.m_bookmarksData)
SaveBoormarkData(m_writer, bookmarkData);
SaveBookmarkData(m_writer, bookmarkData);
// Saving tracks.
for (auto const & trackData : categoryData.m_tracksData)
@ -257,9 +421,14 @@ void KmlWriter::Write(CategoryData const & categoryData)
m_writer << kKmlFooter;
}
KmlParser::KmlParser(CategoryData & data) : m_data(data) { Reset(); }
KmlParser::KmlParser(CategoryData & data)
: m_data(data)
, m_attrCode(StringUtf8Multilang::kUnsupportedLanguageCode)
{
ResetPoint();
}
void KmlParser::Reset()
void KmlParser::ResetPoint()
{
m_name.clear();
m_description.clear();
@ -273,6 +442,13 @@ void KmlParser::Reset()
m_mapStyleId.clear();
m_styleUrlKey.clear();
m_types.clear();
m_boundTracks.clear();
m_localId = 0;
m_trackLayers.clear();
m_trackWidth = kDefaultTrackWidth;
m_icon = BookmarkIcon::None;
m_points.clear();
m_geometryType = GEOMETRY_TYPE_UNKNOWN;
}
@ -281,20 +457,19 @@ bool KmlParser::ParsePoint(std::string const & s, char const * delim, m2::PointD
{
// Order in string is: lon, lat, z.
strings::SimpleTokenizer iter(s, delim);
if (iter)
if (!iter)
return false;
double lon;
if (strings::to_double(*iter, lon) && MercatorBounds::ValidLon(lon) && ++iter)
{
double lon;
if (strings::to_double(*iter, lon) && MercatorBounds::ValidLon(lon) && ++iter)
double lat;
if (strings::to_double(*iter, lat) && MercatorBounds::ValidLat(lat))
{
double lat;
if (strings::to_double(*iter, lat) && MercatorBounds::ValidLat(lat))
{
pt = MercatorBounds::FromLatLon(lat, lon);
return true;
}
pt = MercatorBounds::FromLatLon(lat, lon);
return true;
}
}
return false;
}
@ -318,7 +493,7 @@ void KmlParser::ParseLineCoordinates(std::string const & s, char const * blockSe
m2::PointD pt;
if (ParsePoint(*tupleIter, coordSeparator, pt))
{
if (m_points.size() == 0 || !(pt - m_points.back()).IsAlmostZero())
if (m_points.empty() || !(pt - m_points.back()).IsAlmostZero())
m_points.push_back(std::move(pt));
}
++tupleIter;
@ -333,7 +508,7 @@ bool KmlParser::MakeValid()
{
// Set default name.
if (m_name.empty())
m_name = PointToString(m_org);
m_name[kDefaultLang] = PointToString(m_org);
// Set default pin.
if (m_predefinedColor == PredefinedColor::None)
@ -391,6 +566,11 @@ void KmlParser::AddAttr(std::string const & attr, std::string const & value)
m_styleId = value;
else if (IsValidAttribute(kStyleMap, value, attrInLowerCase))
m_mapStyleId = value;
if (attrInLowerCase == "code")
m_attrCode = StringUtf8Multilang::GetLangIndex(value);
else if (attrInLowerCase == "id")
m_attrId = value;
}
bool KmlParser::IsValidAttribute(std::string const & type, std::string const & value,
@ -416,30 +596,31 @@ void KmlParser::Pop(std::string const & tag)
if (GEOMETRY_TYPE_POINT == m_geometryType)
{
BookmarkData data;
data.m_name[kDefaultLang] = m_name;
data.m_description[kDefaultLang] = m_description;
data.m_name = m_name;
data.m_description = 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;
m_data.m_bookmarksData.emplace_back(std::move(data));
data.m_types = std::move(m_types);
data.m_boundTracks = std::move(m_boundTracks);
m_data.m_bookmarksData.push_back(std::move(data));
}
else if (GEOMETRY_TYPE_LINE == m_geometryType)
{
TrackData data;
data.m_name[kDefaultLang] = m_name;
data.m_description[kDefaultLang] = m_description;
TrackLayer layer;
layer.m_color.m_predefinedColor = PredefinedColor::None;
layer.m_color.m_rgba = m_color;
data.m_layers.emplace_back(std::move(layer));
data.m_localId = m_localId;
data.m_name = m_name;
data.m_description = m_description;
data.m_layers = std::move(m_trackLayers);
data.m_timestamp = m_timestamp;
data.m_points = m_points;
m_data.m_tracksData.emplace_back(std::move(data));
m_data.m_tracksData.push_back(std::move(data));
}
}
Reset();
ResetPoint();
}
else if (tag == kStyle)
{
@ -452,6 +633,17 @@ void KmlParser::Pop(std::string const & tag)
}
}
}
else if (tag == "mwm:additionalLineStyle" || tag == "LineStyle")
{
TrackLayer layer;
layer.m_lineWidth = m_trackWidth;
layer.m_color.m_predefinedColor = PredefinedColor::None;
layer.m_color.m_rgba = m_color;
m_trackLayers.push_back(std::move(layer));
m_trackWidth = kDefaultTrackWidth;
m_color = 0;
}
m_tags.pop_back();
}
@ -466,6 +658,7 @@ void KmlParser::CharData(std::string value)
std::string const & currTag = m_tags[count - 1];
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();
if (prevTag == kDocument)
{
@ -476,11 +669,56 @@ void KmlParser::CharData(std::string value)
else if (currTag == "visibility")
m_data.m_visible = value != "0";
}
else if (prevTag == kExtendedData && ppTag == kDocument)
{
if (currTag == "mwm:author")
{
m_data.m_authorName = value;
m_data.m_authorId = m_attrId;
m_attrId.clear();
}
else if (currTag == "mwm:lastModified")
{
auto const ts = my::StringToTimestamp(value);
if (ts != my::INVALID_TIME_STAMP)
m_data.m_lastModified = std::chrono::system_clock::from_time_t(ts);
}
else if (currTag == "mwm:accessRules")
{
if (value == "Public")
m_data.m_accessRules = AccessRules::Public;
}
}
else if (pppTag == kDocument && ppTag == kExtendedData && currTag == "mwm:lang")
{
if (prevTag == "mwm:name" && m_attrCode > 0)
m_data.m_name[m_attrCode] = value;
else if (prevTag == "mwm:description" && m_attrCode > 0)
m_data.m_description[m_attrCode] = value;
m_attrCode = StringUtf8Multilang::kUnsupportedLanguageCode;
}
else if (pppTag == kDocument && ppTag == kExtendedData && currTag == "mwm:value")
{
if (prevTag == "mwm:tags")
{
m_data.m_tags.push_back(value);
}
else if (prevTag == "mwm:toponyms")
{
m_data.m_toponyms.push_back(value);
}
else if (prevTag == "mwm:languageCodes")
{
auto const lang = StringUtf8Multilang::GetLangIndex(value);
if (lang != StringUtf8Multilang::kUnsupportedLanguageCode)
m_data.m_languageCodes.push_back(lang);
}
}
else if (prevTag == kPlacemark)
{
if (currTag == "name")
{
m_name = value;
m_name[kDefaultLang] = value;
}
else if (currTag == "styleUrl")
{
@ -498,12 +736,21 @@ void KmlParser::CharData(std::string value)
}
else if (currTag == "description")
{
m_description = value;
m_description[kDefaultLang] = value;
}
}
else if (prevTag == "LineStyle" && currTag == "color")
else if (prevTag == "LineStyle" || prevTag == "mwm:additionalLineStyle")
{
ParseColor(value);
if (currTag == "color")
{
ParseColor(value);
}
else if (currTag == "width")
{
double val;
if (strings::to_double(value, val))
m_trackWidth = val;
}
}
else if (ppTag == kStyleMap && prevTag == kPair && currTag == kStyleUrl &&
m_styleUrlKey == "normal")
@ -532,7 +779,7 @@ void KmlParser::CharData(std::string value)
if (currTag == "gx:coord")
ParseLineCoordinates(value, "\n\r\t", " ");
}
else if (prevTag == "ExtendedData")
else if (prevTag == kExtendedData)
{
if (currTag == "mwm:scale")
{
@ -541,6 +788,18 @@ void KmlParser::CharData(std::string value)
scale = 0.0;
m_viewportScale = static_cast<uint8_t>(scale);
}
else if (currTag == "mwm:localId")
{
uint32_t localId;
if (!strings::to_uint(value, localId))
localId = 0;
m_localId = static_cast<LocalId>(localId);
}
else if (currTag == "mwm:icon")
{
//TODO: add new icon types.
//m_icon = ;
}
}
else if (prevTag == "TimeStamp")
{
@ -582,6 +841,25 @@ void KmlParser::CharData(std::string value)
ParseLineCoordinates(value, "\n\r\t", " ");
}
}
else if (pppTag == kPlacemark && ppTag == kExtendedData)
{
if (currTag == "mwm:lang")
{
if (prevTag == "mwm:name" && m_attrCode > 0)
m_name[m_attrCode] = value;
else if (prevTag == "mwm:description" && m_attrCode > 0)
m_description[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);
else if (prevTag == "mwm:boundTracks" && strings::to_uint(value, i))
m_boundTracks.push_back(static_cast<LocalId>(i));
}
}
}
}

View file

@ -78,7 +78,7 @@ private:
GEOMETRY_TYPE_LINE
};
void Reset();
void ResetPoint();
bool ParsePoint(std::string const & s, char const * delim, m2::PointD & pt);
void SetOrigin(std::string const & s);
void ParseLineCoordinates(std::string const & s, char const * blockSeparator,
@ -100,12 +100,21 @@ private:
std::map<std::string, uint32_t> m_styleUrl2Color;
std::map<std::string, std::string> m_mapStyle2Style;
std::string m_name;
std::string m_description;
int8_t m_attrCode;
std::string m_attrId;
LocalizableString m_name;
LocalizableString m_description;
PredefinedColor m_predefinedColor;
Timestamp m_timestamp;
m2::PointD m_org;
uint8_t m_viewportScale;
std::vector<uint32_t> m_types;
std::vector<LocalId> m_boundTracks;
LocalId m_localId;
BookmarkIcon m_icon;
std::vector<TrackLayer> m_trackLayers;
double m_trackWidth;
};
class DeserializerKml