From 73b898476c467d31fdc7215a2874ff89a4d13b28 Mon Sep 17 00:00:00 2001 From: cyber-toad Date: Sat, 1 Jul 2023 18:27:57 +0200 Subject: [PATCH] [gpx] Extract altitude values from GPX Signed-off-by: cyber-toad --- kml/kml_tests/gpx_tests.cpp | 37 +++++++++++- kml/serdes_gpx.cpp | 116 ++++++++++++++++++++++-------------- kml/serdes_gpx.hpp | 7 ++- 3 files changed, 111 insertions(+), 49 deletions(-) diff --git a/kml/kml_tests/gpx_tests.cpp b/kml/kml_tests/gpx_tests.cpp index 8e42599624..97146199c1 100644 --- a/kml/kml_tests/gpx_tests.cpp +++ b/kml/kml_tests/gpx_tests.cpp @@ -79,7 +79,38 @@ UNIT_TEST(Gpx_Test_Route) kml::FileData const dataFromText = loadGpxFromString(input); auto line = dataFromText.m_tracksData[0].m_geometry.m_lines[0]; TEST_EQUAL(line.size(), 3, ()); - TEST_EQUAL(line[0], mercator::FromLatLon(54.23955053156179, 24.114990234375004), ()); + TEST_EQUAL(line[0], geometry::PointWithAltitude(mercator::FromLatLon(54.23955053156179, 24.114990234375004), 131), ()); +} + + +UNIT_TEST(Gpx_Altitude_Issues) +{ + std::string const input = R"( + + + new + Cycling + + + 1.0 + + 2.0 + Wrong + 3 + + + +)"; + + kml::FileData const dataFromText = loadGpxFromString(input); + auto line = dataFromText.m_tracksData[0].m_geometry.m_lines[0]; + TEST_EQUAL(line.size(), 6, ()); + TEST_EQUAL(line[0], geometry::PointWithAltitude(mercator::FromLatLon(1, 1), geometry::kInvalidAltitude), ()); + TEST_EQUAL(line[1], geometry::PointWithAltitude(mercator::FromLatLon(2, 2), 1), ()); + TEST_EQUAL(line[2], geometry::PointWithAltitude(mercator::FromLatLon(3, 3), geometry::kInvalidAltitude), ()); + TEST_EQUAL(line[3], geometry::PointWithAltitude(mercator::FromLatLon(4, 4), 2), ()); + TEST_EQUAL(line[4], geometry::PointWithAltitude(mercator::FromLatLon(5, 5), geometry::kInvalidAltitude), ()); + TEST_EQUAL(line[5], geometry::PointWithAltitude(mercator::FromLatLon(6, 6), 3), ()); } UNIT_TEST(GoMap) @@ -125,8 +156,8 @@ UNIT_TEST(Route) auto line = dataFromFile.m_tracksData[0].m_geometry.m_lines[0]; TEST_EQUAL(line.size(), 2, ()); TEST_EQUAL(dataFromFile.m_categoryData.m_name[kDefaultCode], "Some random route", ()); - TEST_EQUAL(line[0], mercator::FromLatLon(48.20984622935899, 16.376023292541507), ()); - TEST_EQUAL(line[1], mercator::FromLatLon(48.209503040543545, 16.381065845489506), ()); + TEST_EQUAL(line[0], geometry::PointWithAltitude(mercator::FromLatLon(48.20984622935899, 16.376023292541507), 184), ()); + TEST_EQUAL(line[1], geometry::PointWithAltitude(mercator::FromLatLon(48.209503040543545, 16.381065845489506), 187), ()); } UNIT_TEST(Color) diff --git a/kml/serdes_gpx.cpp b/kml/serdes_gpx.cpp index 86b0376c1e..b87df7df0e 100644 --- a/kml/serdes_gpx.cpp +++ b/kml/serdes_gpx.cpp @@ -30,6 +30,7 @@ std::string_view constexpr kGpx = "gpx"; std::string_view constexpr kGarminColor = "gpxx:DisplayColor"; std::string_view constexpr kDesc = "desc"; std::string_view constexpr kMetadata = "metadata"; +std::string_view constexpr kEle = "ele"; int constexpr kInvalidColor = 0; @@ -63,13 +64,14 @@ void GpxParser::ResetPoint() m_trackLayers.clear(); m_geometry.Clear(); m_geometryType = GEOMETRY_TYPE_UNKNOWN; + m_altitude = geometry::kInvalidAltitude; } bool GpxParser::MakeValid() { if (GEOMETRY_TYPE_POINT == m_geometryType) { - if (mercator::ValidX(m_org.x) && mercator::ValidY(m_org.y)) + if (mercator::ValidX(m_org.GetPoint().x) && mercator::ValidY(m_org.GetPoint().y)) { // Set default name. if (m_name.empty()) @@ -101,20 +103,18 @@ bool GpxParser::Push(std::string_view tag) return true; } +bool GpxParser::IsValidCoordinatesPosition() +{ + return GetTagFromEnd(0) == gpx::kWpt || (GetTagFromEnd(0) == gpx::kTrkPt && GetTagFromEnd(1) == gpx::kTrkSeg) || + (GetTagFromEnd(0) == gpx::kRtePt && GetTagFromEnd(1) == gpx::kRte); +} + void GpxParser::AddAttr(std::string const & attr, std::string const & value) { std::string attrInLowerCase = attr; strings::AsciiToLower(attrInLowerCase); - if (GetTagFromEnd(0) == gpx::kWpt) - { - if (attr == "lat") - m_lat = stod(value); - else if (attr == "lon") - m_lon = stod(value); - } - else if ((GetTagFromEnd(0) == gpx::kTrkPt && GetTagFromEnd(1) == gpx::kTrkSeg) || - (GetTagFromEnd(0) == gpx::kRtePt && GetTagFromEnd(1) == gpx::kRte)) + if (IsValidCoordinatesPosition()) { if (attr == "lat") m_lat = stod(value); @@ -215,9 +215,10 @@ void GpxParser::Pop(std::string_view tag) if (tag == gpx::kTrkPt || tag == gpx::kRtePt) { - m2::PointD p = mercator::FromLatLon(m_lat, m_lon); + m2::PointD const p = mercator::FromLatLon(m_lat, m_lon); if (m_line.empty() || !AlmostEqualAbs(m_line.back().GetPoint(), p, kMwmPointAccuracy)) - m_line.emplace_back(std::move(p)); + m_line.emplace_back(p, m_altitude); + m_altitude = geometry::kInvalidAltitude; } else if (tag == gpx::kTrkSeg || tag == gpx::kRte) { @@ -225,7 +226,9 @@ void GpxParser::Pop(std::string_view tag) } else if (tag == gpx::kWpt) { - m_org = mercator::FromLatLon(m_lat, m_lon); + m_org.SetPoint(mercator::FromLatLon(m_lat, m_lon)); + m_org.SetAltitude(m_altitude); + m_altitude = geometry::kInvalidAltitude; } if (tag == gpx::kRte || tag == gpx::kTrkSeg || tag == gpx::kWpt) @@ -279,46 +282,69 @@ void GpxParser::CharData(std::string value) size_t const count = m_tags.size(); if (count > 1 && !value.empty()) { - std::string_view const & currTag = m_tags[count - 1]; - std::string_view const & prevTag = m_tags[count - 2]; + std::string const & currTag = m_tags[count - 1]; + std::string const & prevTag = m_tags[count - 2]; - if (prevTag == gpx::kWpt) - { - if (currTag == gpx::kName) - m_name[kml::kDefaultLang] = value; - else if (currTag == gpx::kDesc) - m_description[kml::kDefaultLang] = value; - } - else if (prevTag == gpx::kTrk || prevTag == gpx::kRte) - { - if (currTag == gpx::kName) - { - m_name[kml::kDefaultLang] = value; - if (m_categoryData->m_name[kml::kDefaultLang].empty()) - m_categoryData->m_name[kml::kDefaultLang] = value; - } - else if (currTag == gpx::kDesc) - { - m_description[kml::kDefaultLang] = value; - if (m_categoryData->m_description[kml::kDefaultLang].empty()) - m_categoryData->m_description[kml::kDefaultLang] = value; - } - } - else if (prevTag == gpx::kMetadata) - { - if (currTag == gpx::kName) - m_categoryData->m_name[kml::kDefaultLang] = value; - else if (currTag == gpx::kDesc) - m_categoryData->m_description[kml::kDefaultLang] = value; - } - if (currTag == gpx::kGarminColor) + if (currTag == gpx::kName) + ParseName(value, prevTag); + else if (currTag == gpx::kDesc) + ParseDescription(value, prevTag); + else if (currTag == gpx::kGarminColor) ParseGarminColor(value); else if (currTag == gpx::kOsmandColor) ParseOsmandColor(value); else if (currTag == gpx::kColor) ParseColor(value); + else if (currTag == gpx::kEle) + ParseAltitude(value); } } + +void GpxParser::ParseDescription(std::string const & value, std::string const & prevTag) +{ + if (prevTag == kWpt) + { + m_description[kDefaultLang] = value; + } + else if (prevTag == kTrk || prevTag == kRte) + { + m_description[kDefaultLang] = value; + if (m_categoryData->m_description[kDefaultLang].empty()) + m_categoryData->m_description[kDefaultLang] = value; + } + else if (prevTag == kMetadata) + { + m_categoryData->m_description[kDefaultLang] = value; + } +} + +void GpxParser::ParseName(std::string const & value, std::string const & prevTag) +{ + if (prevTag == kWpt) + { + m_name[kDefaultLang] = value; + } + else if (prevTag == kTrk || prevTag == kRte) + { + m_name[kDefaultLang] = value; + if (m_categoryData->m_name[kDefaultLang].empty()) + m_categoryData->m_name[kDefaultLang] = value; + } + else if (prevTag == kMetadata) + { + m_categoryData->m_name[kDefaultLang] = value; + } +} + +void GpxParser::ParseAltitude(std::string const & value) +{ + double rawAltitude; + if (strings::to_double(value, rawAltitude)) + m_altitude = static_cast(round(rawAltitude)); + else + m_altitude = geometry::kInvalidAltitude; +} + } // namespace gpx DeserializerGpx::DeserializerGpx(FileData & fileData) diff --git a/kml/serdes_gpx.hpp b/kml/serdes_gpx.hpp index 3f85a1fb7c..49307fec17 100644 --- a/kml/serdes_gpx.hpp +++ b/kml/serdes_gpx.hpp @@ -43,6 +43,7 @@ private: void ParseColor(std::string const & value); void ParseGarminColor(std::string const & value); void ParseOsmandColor(std::string const & value); + bool IsValidCoordinatesPosition(); FileData & m_data; CategoryData m_compilationData; @@ -57,14 +58,18 @@ private: LocalizableString m_name; LocalizableString m_description; PredefinedColor m_predefinedColor; - m2::PointD m_org; + geometry::PointWithAltitude m_org; double m_lat; double m_lon; + geometry::Altitude m_altitude; MultiGeometry::LineT m_line; LocalizableString m_customName; std::vector m_trackLayers; + void ParseName(std::string const & value, std::string const & prevTag); + void ParseDescription(std::string const & value, std::string const & prevTag); + void ParseAltitude(std::string const & value); }; } // namespace gpx