diff --git a/kml/serdes.cpp b/kml/serdes.cpp
index 5e576f56ae..233847cc86 100644
--- a/kml/serdes.cpp
+++ b/kml/serdes.cpp
@@ -438,7 +438,7 @@ void SaveBookmarkData(Writer & writer, BookmarkData const & bookmarkData)
auto const style = GetStyleForPredefinedColor(bookmarkData.m_color.m_predefinedColor);
writer << kIndent4 << "#" << style << "\n"
- << kIndent4 << "" << PointToString(bookmarkData.m_point)
+ << kIndent4 << "" << PointToLineString(bookmarkData.m_point)
<< "\n";
SaveBookmarkExtendedData(writer, bookmarkData);
@@ -455,43 +455,103 @@ void SaveTrackLayer(Writer & writer, TrackLayer const & layer,
writer << indent << "" << strings::to_string(layer.m_lineWidth) << "\n";
}
-void SaveTrackGeometry(Writer & writer, MultiGeometry const & geom)
+void SaveLineStrings(Writer & writer, MultiGeometry const & geom)
{
- size_t const sz = geom.m_lines.size();
- if (sz == 0)
- {
- ASSERT(false, ());
- return;
- }
-
auto linesIndent = kIndent4;
- if (sz > 1)
+ auto const lineStringsSize = geom.GetNumberOfLinesWithouTimestamps();
+
+ if (lineStringsSize > 1)
{
linesIndent = kIndent8;
writer << kIndent4 << "\n";
}
- for (auto const & e : geom.m_lines)
+ for (size_t lineIndex = 0; lineIndex < geom.m_lines.size(); ++lineIndex)
{
- if (e.empty())
+ auto const & line = geom.m_lines[lineIndex];
+ if (line.empty())
{
- ASSERT(false, ());
+ LOG(LERROR, ("Unexpected empty Track"));
continue;
}
+ // Skip the tracks with the timestamps when writing the points.
+ if (geom.HasTimestampsFor(lineIndex))
+ continue;
+
writer << linesIndent << "";
- writer << PointToString(e[0]);
- for (size_t i = 1; i < e.size(); ++i)
- writer << " " << PointToString(e[i]);
+ writer << PointToLineString(line[0]);
+ for (size_t pointIndex = 1; pointIndex < line.size(); ++pointIndex)
+ writer << " " << PointToLineString(line[pointIndex]);
writer << "\n";
}
- if (sz > 1)
+ if (lineStringsSize > 1)
writer << kIndent4 << "\n";
}
+void SaveGxTracks(Writer & writer, MultiGeometry const & geom)
+{
+ auto linesIndent = kIndent4;
+ auto const gxTracksSize = geom.GetNumberOfLinesWithTimestamps();
+
+ if (gxTracksSize > 1)
+ {
+ linesIndent = kIndent8;
+ writer << kIndent4 << "\n";
+ /// @TODO(KK): add the absolute if needed
+ }
+
+ for (size_t lineIndex = 0; lineIndex < geom.m_lines.size(); ++lineIndex)
+ {
+ auto const & line = geom.m_lines[lineIndex];
+ if (line.empty())
+ {
+ LOG(LERROR, ("Unexpected empty Track"));
+ continue;
+ }
+
+ // Skip the tracks without the timestamps when writing the points.
+ if (!geom.HasTimestampsFor(lineIndex))
+ continue;
+
+ writer << linesIndent << "\n";
+ /// @TODO(KK): add the absolute if needed
+
+ auto const & timestampsForLine = geom.m_timestamps[lineIndex];
+ if (line.size() != timestampsForLine.size())
+ CHECK_EQUAL(line.size(), timestampsForLine.size(), ());
+
+ for (size_t pointIndex = 0; pointIndex < line.size(); ++pointIndex)
+ writer << linesIndent << kIndent4 << "" << base::SecondsSinceEpochToString(timestampsForLine[pointIndex]) << "\n";
+
+ for (const auto & point : line)
+ writer << linesIndent << kIndent4 << "" << PointToGxString(point) << "\n";
+
+ writer << linesIndent << "\n";
+ }
+
+ if (gxTracksSize > 1)
+ writer << kIndent4 << "\n";
+}
+
+void SaveTrackGeometry(Writer & writer, MultiGeometry const & geom)
+{
+ size_t const sz = geom.m_lines.size();
+ if (sz == 0)
+ {
+ LOG(LERROR, ("Unexpected empty MultiGeometry"));
+ return;
+ }
+
+ CHECK_EQUAL(geom.m_lines.size(), geom.m_timestamps.size(), ("Number of coordinates and timestamps should match"));
+
+ SaveLineStrings(writer, geom);
+ SaveGxTracks(writer, geom);
+}
+
void SaveTrackExtendedData(Writer & writer, TrackData const & trackData)
{
writer << kIndent4 << kExtendedDataHeader;
@@ -547,7 +607,6 @@ void SaveTrackData(Writer & writer, TrackData const & trackData)
}
SaveTrackGeometry(writer, trackData.m_geometry);
-
SaveTrackExtendedData(writer, trackData);
writer << kIndent2 << "\n";
@@ -689,7 +748,10 @@ void KmlParser::ParseLineString(std::string const & s)
ParseAndAddPoints(line, s, " \n\r\t", ",");
if (line.size() > 1)
+ {
m_geometry.m_lines.push_back(std::move(line));
+ m_geometry.m_timestamps.emplace_back();
+ }
}
bool KmlParser::MakeValid()
@@ -700,7 +762,7 @@ bool KmlParser::MakeValid()
{
// Set default name.
if (m_name.empty() && m_featureTypes.empty())
- m_name[kDefaultLang] = PointToString(m_org);
+ m_name[kDefaultLang] = PointToLineString(m_org);
// Set default pin.
if (m_predefinedColor == PredefinedColor::None)
@@ -769,6 +831,7 @@ bool KmlParser::Push(std::string movedTag)
{
m_geometryType = GEOMETRY_TYPE_LINE;
m_geometry.m_lines.emplace_back();
+ m_geometry.m_timestamps.emplace_back();
}
return true;
}
@@ -973,7 +1036,12 @@ void KmlParser::CharData(std::string & value)
{
if (!IsTrack(prevTag))
return false;
-
+ if (currTag == "when")
+ {
+ auto & timestamps = m_geometry.m_timestamps;
+ ASSERT(!timestamps.empty(), ());
+ timestamps.back().emplace_back(base::StringToTimestamp(value));
+ }
if (currTag == "coord" || currTag == "gx:coord")
{
auto & lines = m_geometry.m_lines;
diff --git a/kml/serdes_common.cpp b/kml/serdes_common.cpp
index b93bf711e8..af66f94b87 100644
--- a/kml/serdes_common.cpp
+++ b/kml/serdes_common.cpp
@@ -6,7 +6,7 @@
namespace kml
{
-std::string PointToString(m2::PointD const & org)
+std::string PointToString(m2::PointD const & org, char const separator)
{
double const lon = mercator::XToLon(org.x);
double const lat = mercator::YToLat(org.y);
@@ -14,15 +14,24 @@ std::string PointToString(m2::PointD const & org)
std::ostringstream ss;
ss.precision(8);
- ss << lon << "," << lat;
+ ss << lon << separator << lat;
return ss.str();
}
-std::string PointToString(geometry::PointWithAltitude const & pt)
+std::string PointToLineString(geometry::PointWithAltitude const & pt)
{
+ char constexpr kSeparator = ',';
if (pt.GetAltitude() != geometry::kInvalidAltitude)
- return PointToString(pt.GetPoint()) + "," + strings::to_string(pt.GetAltitude());
- return PointToString(pt.GetPoint());
+ return PointToString(pt.GetPoint(), kSeparator) + kSeparator + strings::to_string(pt.GetAltitude());
+ return PointToString(pt.GetPoint(), kSeparator);
+}
+
+std::string PointToGxString(geometry::PointWithAltitude const & pt)
+{
+ char constexpr kSeparator = ' ';
+ if (pt.GetAltitude() != geometry::kInvalidAltitude)
+ return PointToString(pt.GetPoint(), kSeparator) + kSeparator + strings::to_string(pt.GetAltitude());
+ return PointToString(pt.GetPoint(), kSeparator);
}
void SaveStringWithCDATA(Writer & writer, std::string s)
diff --git a/kml/serdes_common.hpp b/kml/serdes_common.hpp
index 19032724fa..2ad192244d 100644
--- a/kml/serdes_common.hpp
+++ b/kml/serdes_common.hpp
@@ -20,9 +20,10 @@ uint32_t ToRGBA(Channel red, Channel green, Channel blue, Channel alpha)
static_cast(blue) << 8 | static_cast(alpha);
}
-std::string PointToString(m2::PointD const & org);
+std::string PointToString(m2::PointD const & org, char const separator);
-std::string PointToString(geometry::PointWithAltitude const & pt);
+std::string PointToLineString(geometry::PointWithAltitude const & pt);
+std::string PointToGxString(geometry::PointWithAltitude const & pt);
void SaveStringWithCDATA(Writer & writer, std::string s);
diff --git a/kml/serdes_gpx.cpp b/kml/serdes_gpx.cpp
index 38968beb91..7c1a59bd02 100644
--- a/kml/serdes_gpx.cpp
+++ b/kml/serdes_gpx.cpp
@@ -74,7 +74,7 @@ bool GpxParser::MakeValid()
{
// Set default name.
if (m_name.empty())
- m_name = kml::PointToString(m_org);
+ m_name = kml::PointToLineString(m_org);
// Set default pin.
if (m_predefinedColor == PredefinedColor::None)