[serdes] [kml] save KML track in the gxt:Track format if it has timestamps
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
This commit is contained in:
parent
648a8b1a84
commit
cf65dc0614
5 changed files with 95 additions and 7 deletions
|
@ -13,6 +13,8 @@
|
|||
#include "base/string_utils.hpp"
|
||||
#include "base/timer.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace kml
|
||||
{
|
||||
namespace
|
||||
|
@ -455,7 +457,7 @@ void SaveTrackLayer(Writer & writer, TrackLayer const & layer,
|
|||
writer << indent << "<width>" << strings::to_string(layer.m_lineWidth) << "</width>\n";
|
||||
}
|
||||
|
||||
void SaveTrackGeometry(Writer & writer, MultiGeometry const & geom)
|
||||
void SaveTrackGeometryAsLineString(Writer & writer, MultiGeometry const & geom)
|
||||
{
|
||||
size_t const sz = geom.m_lines.size();
|
||||
if (sz == 0)
|
||||
|
@ -478,18 +480,66 @@ void SaveTrackGeometry(Writer & writer, MultiGeometry const & geom)
|
|||
ASSERT(false, ());
|
||||
continue;
|
||||
}
|
||||
|
||||
writer << linesIndent << "<LineString><coordinates>";
|
||||
|
||||
writer << PointToString(e[0]);
|
||||
for (size_t i = 1; i < e.size(); ++i)
|
||||
writer << " " << PointToString(e[i]);
|
||||
|
||||
writer << "</coordinates></LineString>\n";
|
||||
}
|
||||
|
||||
if (sz > 1)
|
||||
writer << kIndent4 << "</MultiGeometry>\n";
|
||||
}
|
||||
|
||||
void SaveTrackGeometryAsGxTrack(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)
|
||||
{
|
||||
linesIndent = kIndent8;
|
||||
writer << kIndent4 << "<gx:MultiTrack>\n";
|
||||
/// @TODO(KK): add the <altitudeMode>absolute</altitudeMode> if needed
|
||||
}
|
||||
|
||||
for (size_t lineIndex = 0; lineIndex < geom.m_lines.size(); ++lineIndex)
|
||||
{
|
||||
auto const & line = geom.m_lines[lineIndex];
|
||||
if (line.empty())
|
||||
{
|
||||
ASSERT(false, ());
|
||||
continue;
|
||||
}
|
||||
|
||||
writer << linesIndent << "<gx:Track>\n";
|
||||
/// @TODO(KK): add the <altitudeMode>absolute</altitudeMode> if needed
|
||||
if (geom.HasTimestampsFor(lineIndex))
|
||||
{
|
||||
/// @TODO(KK): Is it the proper way to handle timestamps for tracks sizes mismatch? Maybe it will be better to skip the timestamps section and throws an error if smth happens?
|
||||
CHECK_EQUAL(geom.m_lines.size(), geom.m_timestamps.size(), ("Timestamps size mismatch"));
|
||||
auto const & timestampsForLine = geom.m_timestamps[lineIndex];
|
||||
for (size_t pointIndex = 0; pointIndex < line.size(); ++pointIndex)
|
||||
{
|
||||
CHECK_EQUAL(line.size(), timestampsForLine.size(), ("Timestamps size mismatch for track:", lineIndex));
|
||||
auto const timeStr = base::SecondsSinceEpochToString(timestampsForLine[pointIndex]);
|
||||
writer << linesIndent << kIndent4 << "<when>" << timeStr << "</when>\n";
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto & point : line)
|
||||
writer << linesIndent << kIndent4 << "<gx:coord>" << PointToGxString(point) << "</gx:coord>\n";
|
||||
|
||||
writer << linesIndent << "</gx:Track>\n";
|
||||
}
|
||||
|
||||
if (sz > 1)
|
||||
writer << kIndent4 << "</MultiGeometry>\n";
|
||||
writer << kIndent4 << "</gx:MultiTrack>\n";
|
||||
}
|
||||
|
||||
void SaveTrackExtendedData(Writer & writer, TrackData const & trackData)
|
||||
|
@ -546,7 +596,10 @@ void SaveTrackData(Writer & writer, TrackData const & trackData)
|
|||
<< "</when></TimeStamp>\n";
|
||||
}
|
||||
|
||||
SaveTrackGeometry(writer, trackData.m_geometry);
|
||||
if (trackData.m_geometry.HasTimestamps())
|
||||
SaveTrackGeometryAsGxTrack(writer, trackData.m_geometry);
|
||||
else
|
||||
SaveTrackGeometryAsLineString(writer, trackData.m_geometry);
|
||||
|
||||
SaveTrackExtendedData(writer, trackData);
|
||||
|
||||
|
@ -679,6 +732,13 @@ void KmlParser::ParseAndAddPoints(MultiGeometry::LineT & line, std::string_view
|
|||
});
|
||||
}
|
||||
|
||||
void KmlParser::ParseAndAddTimestamps(MultiGeometry::TimeT & timestamps, std::string_view s,
|
||||
char const * blockSeparator)
|
||||
{
|
||||
auto const ts = base::StringToTimestamp(std::string{s});
|
||||
timestamps.emplace_back(ts);
|
||||
}
|
||||
|
||||
void KmlParser::ParseLineString(std::string const & s)
|
||||
{
|
||||
// If m_org is not empty, then it's still a Bookmark but with track data
|
||||
|
@ -769,6 +829,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 +1034,12 @@ void KmlParser::CharData(std::string & value)
|
|||
{
|
||||
if (!IsTrack(prevTag))
|
||||
return false;
|
||||
|
||||
if (currTag == "when")
|
||||
{
|
||||
auto & timestamps = m_geometry.m_timestamps;
|
||||
ASSERT(!timestamps.empty(), ());
|
||||
ParseAndAddTimestamps(timestamps.back(), value, "\n\r\t");
|
||||
}
|
||||
if (currTag == "coord" || currTag == "gx:coord")
|
||||
{
|
||||
auto & lines = m_geometry.m_lines;
|
||||
|
|
|
@ -83,6 +83,8 @@ private:
|
|||
void SetOrigin(std::string const & s);
|
||||
static void ParseAndAddPoints(MultiGeometry::LineT & line, std::string_view s,
|
||||
char const * blockSeparator, char const * coordSeparator);
|
||||
void ParseAndAddTimestamps(MultiGeometry::TimeT & timestamps, std::string_view s,
|
||||
char const * blockSeparator);
|
||||
void ParseLineString(std::string const & s);
|
||||
|
||||
bool MakeValid();
|
||||
|
|
|
@ -25,6 +25,13 @@ std::string PointToString(geometry::PointWithAltitude const & pt)
|
|||
return PointToString(pt.GetPoint());
|
||||
}
|
||||
|
||||
std::string PointToGxString(geometry::PointWithAltitude const & pt)
|
||||
{
|
||||
std::string str = PointToString(pt);
|
||||
std::replace(str.begin(), str.end(), ',', ' ');
|
||||
return str;
|
||||
}
|
||||
|
||||
void SaveStringWithCDATA(Writer & writer, std::string s)
|
||||
{
|
||||
if (s.empty())
|
||||
|
|
|
@ -23,6 +23,7 @@ uint32_t ToRGBA(Channel red, Channel green, Channel blue, Channel alpha)
|
|||
std::string PointToString(m2::PointD const & org);
|
||||
|
||||
std::string PointToString(geometry::PointWithAltitude const & pt);
|
||||
std::string PointToGxString(geometry::PointWithAltitude const & pt);
|
||||
|
||||
void SaveStringWithCDATA(Writer & writer, std::string s);
|
||||
|
||||
|
|
|
@ -376,6 +376,18 @@ struct MultiGeometry
|
|||
m_lines.back().assign(lst);
|
||||
}
|
||||
|
||||
bool HasTimestamps() const
|
||||
{
|
||||
if (m_timestamps.empty())
|
||||
return false;
|
||||
for (auto const & timestamp : m_timestamps)
|
||||
{
|
||||
if (!timestamp.empty())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasTimestampsFor(size_t lineIndex) const
|
||||
{
|
||||
return !(m_timestamps.empty() || lineIndex >= m_timestamps.size() || m_timestamps[lineIndex].empty());
|
||||
|
|
Reference in a new issue