diff --git a/editor/editor_tests/xml_feature_test.cpp b/editor/editor_tests/xml_feature_test.cpp index aa7bfd82ce..6ccc0e713f 100644 --- a/editor/editor_tests/xml_feature_test.cpp +++ b/editor/editor_tests/xml_feature_test.cpp @@ -236,3 +236,20 @@ UNIT_TEST(XMLFeature_FromXmlNode) TEST_EQUAL(copy.GetAttribute("id"), "4", ()); TEST_EQUAL(copy.GetTagValue("amenity"), "fountain", ()); } + +UNIT_TEST(XMLFeature_Geometry) +{ + XMLFeature::TMercatorGeometry const geometry = { + {28.7206411, 3.7182409}, + {46.7569003, 47.0774689}, + {22.5909217, 41.6994874}, + {14.7537008, 17.7788229}, + {55.1261701, 10.3199476}, + {28.6519654, 50.0305930}, + {28.7206411, 3.7182409} + }; + + XMLFeature feature(XMLFeature::Type::Way); + feature.SetGeometry(geometry); + TEST_EQUAL(feature.GetGeometry(), geometry, ()); +} diff --git a/editor/xml_feature.cpp b/editor/xml_feature.cpp index b3b46a210e..b5729a98cb 100644 --- a/editor/xml_feature.cpp +++ b/editor/xml_feature.cpp @@ -26,7 +26,7 @@ pugi::xml_node FindTag(pugi::xml_document const & document, string const & key) return document.select_node(("//tag[@k='" + key + "']").data()).node(); } -ms::LatLon PointFromLatLon(pugi::xml_node const & node) +ms::LatLon GetLatLonFromNode(pugi::xml_node const & node) { ms::LatLon ll; if (!strings::to_double(node.attribute("lat").value(), ll.lat)) @@ -36,6 +36,16 @@ ms::LatLon PointFromLatLon(pugi::xml_node const & node) return ll; } +m2::PointD GetMercatorPointFromNode(pugi::xml_node const & node) +{ + m2::PointD p; + if (!strings::to_double(node.attribute("x").value(), p.x)) + MYTHROW(editor::NoXY, ("Can't parse x attribute: " + string(node.attribute("x").value()))); + if (!strings::to_double(node.attribute("y").value(), p.y)) + MYTHROW(editor::NoXY, ("Can't parse y attribute: " + string(node.attribute("y").value()))); + return p; +} + void ValidateElement(pugi::xml_node const & nodeOrWay) { if (!nodeOrWay) @@ -43,14 +53,13 @@ void ValidateElement(pugi::xml_node const & nodeOrWay) string const type = nodeOrWay.name(); if (type == kNodeType) - UNUSED_VALUE(PointFromLatLon(nodeOrWay)); + UNUSED_VALUE(GetLatLonFromNode(nodeOrWay)); else if (type != kWayType) MYTHROW(editor::InvalidXML, ("XMLFeature does not support root tag", type)); if (!nodeOrWay.attribute(kTimestamp)) MYTHROW(editor::NoTimestamp, ("Node has no timestamp attribute")); } - } // namespace namespace editor @@ -157,7 +166,7 @@ void XMLFeature::ApplyPatch(XMLFeature const & featureWithChanges) ms::LatLon XMLFeature::GetCenter() const { - return PointFromLatLon(GetRootNode()); + return GetLatLonFromNode(GetRootNode()); } void XMLFeature::SetCenter(ms::LatLon const & ll) @@ -172,6 +181,32 @@ void XMLFeature::SetCenter(m2::PointD const & mercatorCenter) SetCenter(MercatorBounds::ToLatLon(mercatorCenter)); } +XMLFeature::TMercatorGeometry XMLFeature::GetGeometry() const +{ + ASSERT_EQUAL(GetType(), Type::Way, ("Only ways have geometry")); + TMercatorGeometry geometry; + for (auto const xCenter : GetRootNode().select_nodes("nd")) + { + ASSERT(xCenter.node(), ("no nd attribute.")); + geometry.emplace_back(GetMercatorPointFromNode(xCenter.node())); + } + return geometry; +} + +/// Geometry points are now stored in nodes like in osm . +/// But they are not the same as osm's. I.e. osm's one stores reference to a +/// with it's own data and lat, lon. Here we store only cooridanes in mercator. +void XMLFeature::SetGeometry(TMercatorGeometry const & geometry) +{ + ASSERT_EQUAL(GetType(), Type::Way, ("Only ways have geometry")); + for (auto const & point : geometry) + { + auto nd = GetRootNode().append_child("nd"); + nd.append_attribute("x") = strings::to_string_dac(point.x, kLatLonTolerance).data(); + nd.append_attribute("y") = strings::to_string_dac(point.y, kLatLonTolerance).data(); + } +} + string XMLFeature::GetName(string const & lang) const { auto const suffix = (lang == kDefaultLang || lang.empty()) ? "" : ":" + lang; @@ -332,4 +367,12 @@ string DebugPrint(XMLFeature const & feature) return ost.str(); } +string DebugPrint(XMLFeature::Type const type) +{ + switch (type) + { + case XMLFeature::Type::Node: return "Node"; + case XMLFeature::Type::Way: return "Way"; + } +} } // namespace editor diff --git a/editor/xml_feature.hpp b/editor/xml_feature.hpp index 0c368f3226..83578a89e9 100644 --- a/editor/xml_feature.hpp +++ b/editor/xml_feature.hpp @@ -18,6 +18,7 @@ namespace editor DECLARE_EXCEPTION(XMLFeatureError, RootException); DECLARE_EXCEPTION(InvalidXML, XMLFeatureError); DECLARE_EXCEPTION(NoLatLon, XMLFeatureError); +DECLARE_EXCEPTION(NoXY, XMLFeatureError); DECLARE_EXCEPTION(NoTimestamp, XMLFeatureError); DECLARE_EXCEPTION(NoHeader, XMLFeatureError); @@ -34,6 +35,8 @@ public: Way }; + using TMercatorGeometry = vector; + /// Creates empty node or way. XMLFeature(Type const type); XMLFeature(string const & xml); @@ -60,6 +63,12 @@ public: void SetCenter(ms::LatLon const & ll); void SetCenter(m2::PointD const & mercatorCenter); + TMercatorGeometry GetGeometry() const; + + /// Sets geometry in mercator to match against FeatureType's geometry in mwm + /// when megrating to a new mwm build. + void SetGeometry(TMercatorGeometry const & geometry); + string GetName(string const & lang) const; string GetName(uint8_t const langCode = StringUtf8Multilang::DEFAULT_CODE) const; @@ -133,5 +142,5 @@ private: }; string DebugPrint(XMLFeature const & feature); - +string DebugPrint(XMLFeature::Type const type); } // namespace editor diff --git a/indexer/feature.cpp b/indexer/feature.cpp index 54eaa6bf9a..5789fe9481 100644 --- a/indexer/feature.cpp +++ b/indexer/feature.cpp @@ -96,10 +96,15 @@ editor::XMLFeature FeatureType::ToXML() const ? editor::XMLFeature::Type::Node : editor::XMLFeature::Type::Way); - // Only Poins are completely serialized and deserialized. - // Other types could only be patched. if (GetFeatureType() == feature::GEOM_POINT) + { feature.SetCenter(GetCenter()); + } + else + { + ParseTriangles(BEST_GEOMETRY); + feature.SetGeometry({begin(m_triangles), end(m_triangles)}); + } ForEachName([&feature](uint8_t const & lang, string const & name) { @@ -124,7 +129,7 @@ editor::XMLFeature FeatureType::ToXML() const // feature.SetTagValue(tag.first, tag.second); // } - for (auto const type : m_metadata.GetPresentTypes()) + for (auto const type : GetMetadata().GetPresentTypes()) { auto const attributeName = DebugPrint(static_cast(type)); feature.SetTagValue(attributeName, m_metadata.Get(type));