diff options
author | Sergey Magidovich <mgsergio@mapswithme.com> | 2016-03-21 14:40:44 +0300 |
---|---|---|
committer | Sergey Yershov <yershov@corp.mail.ru> | 2016-03-23 16:59:26 +0300 |
commit | d4aa42b8a4e3bd8272f4615e83e43150f34ba834 (patch) | |
tree | 0c2ca9dd925b075363bf728cd3330e76376cd319 /editor | |
parent | 36fc19a3d2554bdc931b79bbf20d7d0b3a5b8213 (diff) |
Match against relations.
Diffstat (limited to 'editor')
-rw-r--r-- | editor/changeset_wrapper.cpp | 53 | ||||
-rw-r--r-- | editor/changeset_wrapper.hpp | 5 | ||||
-rw-r--r-- | editor/editor_tests/osm_feature_matcher_test.cpp | 252 | ||||
-rw-r--r-- | editor/osm_feature_matcher.cpp | 98 | ||||
-rw-r--r-- | editor/osm_feature_matcher.hpp | 4 |
5 files changed, 358 insertions, 54 deletions
diff --git a/editor/changeset_wrapper.cpp b/editor/changeset_wrapper.cpp index 3a99160fb5..e844e23218 100644 --- a/editor/changeset_wrapper.cpp +++ b/editor/changeset_wrapper.cpp @@ -15,6 +15,20 @@ using editor::XMLFeature; +namespace +{ +m2::RectD GetBoundingRect(vector<m2::PointD> const & geometry) +{ + m2::RectD rect; + for (auto const & p : geometry) + { + auto const latLon = MercatorBounds::ToLatLon(p); + rect.Add({latLon.lon, latLon.lat}); + } + return rect; +} +} // namespace + namespace pugi { string DebugPrint(xml_document const & doc) @@ -27,7 +41,6 @@ string DebugPrint(xml_document const & doc) namespace osm { - ChangesetWrapper::ChangesetWrapper(TKeySecret const & keySecret, ServerApi06::TKeyValueTags const & comments) noexcept : m_changesetComments(comments), m_api(OsmOAuth::ServerAuth(keySecret)) @@ -59,6 +72,16 @@ void ChangesetWrapper::LoadXmlFromOSM(ms::LatLon const & ll, pugi::xml_document MYTHROW(OsmXmlParseException, ("Can't parse OSM server response for GetXmlFeaturesAtLatLon request", response.second)); } +void ChangesetWrapper::LoadXmlFromOSM(m2::RectD const & rect, pugi::xml_document & doc) +{ + auto const response = m_api.GetXmlFeaturesInRect(rect); + if (response.first != OsmOAuth::HTTP::OK) + MYTHROW(HttpErrorException, ("HTTP error", response, "with GetXmlFeaturesInRect", rect)); + + if (pugi::status_ok != doc.load(response.second.c_str()).status) + MYTHROW(OsmXmlParseException, ("Can't parse OSM server response for GetXmlFeaturesInRect request", response.second)); +} + XMLFeature ChangesetWrapper::GetMatchingNodeFeatureFromOSM(m2::PointD const & center) { // Match with OSM node. @@ -80,6 +103,7 @@ XMLFeature ChangesetWrapper::GetMatchingNodeFeatureFromOSM(m2::PointD const & ce XMLFeature ChangesetWrapper::GetMatchingAreaFeatureFromOSM(vector<m2::PointD> const & geometry) { // TODO: Make two/four requests using points on inscribed rectagle. + bool hasRelation = false; for (auto const & pt : geometry) { ms::LatLon const ll = MercatorBounds::ToLatLon(pt); @@ -87,11 +111,32 @@ XMLFeature ChangesetWrapper::GetMatchingAreaFeatureFromOSM(vector<m2::PointD> co // Throws! LoadXmlFromOSM(ll, doc); - pugi::xml_node const bestWay = GetBestOsmWay(doc, geometry); - if (bestWay.empty()) + if (doc.select_node("osm/relation")) + { + auto const rect = GetBoundingRect(geometry); + LoadXmlFromOSM(rect, doc); + hasRelation = true; + } + + pugi::xml_node const bestWayOrRelation = GetBestOsmWayOrRelation(doc, geometry); + if (!bestWayOrRelation) + { + if (hasRelation) + break; continue; + } + + if (strcmp(bestWayOrRelation.name(), "relation") == 0) + { + stringstream sstr; + bestWayOrRelation.print(sstr); + LOG(LDEBUG, ("Relation is the best match", sstr.str())); + MYTHROW(RelationFeatureAreNotSupportedException, + ("Got relation as the best matching.")); + } - XMLFeature const way(bestWay); + // TODO: rename to wayOrRelation when relations are handled. + XMLFeature const way(bestWayOrRelation); ASSERT(way.IsArea(), ("Best way must be an area.")); // AlexZ: TODO: Check that this way is really match our feature. diff --git a/editor/changeset_wrapper.hpp b/editor/changeset_wrapper.hpp index d1665276f5..0eefcf4788 100644 --- a/editor/changeset_wrapper.hpp +++ b/editor/changeset_wrapper.hpp @@ -6,6 +6,7 @@ #include "editor/xml_feature.hpp" #include "geometry/point2d.hpp" +#include "geometry/rect2d.hpp" #include "std/set.hpp" #include "std/vector.hpp" @@ -14,7 +15,6 @@ class FeatureType; namespace osm { - struct ClientToken; class ChangesetWrapper @@ -28,6 +28,8 @@ public: DECLARE_EXCEPTION(CreateChangeSetFailedException, ChangesetWrapperException); DECLARE_EXCEPTION(ModifyNodeFailedException, ChangesetWrapperException); DECLARE_EXCEPTION(LinearFeaturesAreNotSupportedException, ChangesetWrapperException); + // TODO: Remove this when relations are handled properly. + DECLARE_EXCEPTION(RelationFeatureAreNotSupportedException, ChangesetWrapperException); ChangesetWrapper(TKeySecret const & keySecret, ServerApi06::TKeyValueTags const & comments) noexcept; ~ChangesetWrapper(); @@ -51,6 +53,7 @@ private: /// Unfortunately, pugi can't return xml_documents from methods. /// Throws exceptions from above list. void LoadXmlFromOSM(ms::LatLon const & ll, pugi::xml_document & doc); + void LoadXmlFromOSM(m2::RectD const & rect, pugi::xml_document & doc); ServerApi06::TKeyValueTags m_changesetComments; ServerApi06 m_api; diff --git a/editor/editor_tests/osm_feature_matcher_test.cpp b/editor/editor_tests/osm_feature_matcher_test.cpp index f6ac0c3e47..d0a8361b6c 100644 --- a/editor/editor_tests/osm_feature_matcher_test.cpp +++ b/editor/editor_tests/osm_feature_matcher_test.cpp @@ -5,7 +5,9 @@ #include "3party/pugixml/src/pugixml.hpp" -static char const * const osmRawResponseWay = R"SEP( +namespace +{ +char const * const osmRawResponseWay = R"SEP( <osm version="0.6" generator="CGImap 0.4.0 (22123 thorn-03.openstreetmap.org)" copyright="OpenStreetMap and contributors"> <bounds minlat="53.8976570" minlon="27.5576615" maxlat="53.8976570" maxlon="27.5576615"/> <node id="277171984" visible="true" version="2" changeset="20577443" timestamp="2014-02-15T14:37:39Z" user="iglezz" uid="450366" lat="53.8978034" lon="27.5577642"/> @@ -52,7 +54,7 @@ static char const * const osmRawResponseWay = R"SEP( </osm> )SEP"; -static char const * const osmRawResponseNode = R"SEP( +char const * const osmRawResponseNode = R"SEP( <osm version="0.6" generator="CGImap 0.4.0 (5501 thorn-02.openstreetmap.org)" copyright="OpenStreetMap and contributors"> <bounds minlat="53.8977000" minlon="27.5578900" maxlat="53.8977700" maxlon="27.5579800"/> <node id="2673014342" visible="true" version="1" changeset="20577443" timestamp="2014-02-15T14:37:11Z" user="iglezz" uid="450366" lat="53.8976095" lon="27.5579360"/> @@ -84,6 +86,202 @@ static char const * const osmRawResponseNode = R"SEP( </osm> )SEP"; +char const * const osmRawResponseRelation = R"SEP( +<?xml version="1.0" encoding="UTF-8"?> +<osm version="0.6" generator="CGImap 0.4.0 (22560 thorn-01.openstreetmap.org)" copyright="OpenStreetMap and contributors"> + <bounds minlat="55.7509200" minlon="37.6397200" maxlat="55.7515400" maxlon="37.6411300"/> + <node id="271892032" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:35Z" user="Scondo" uid="421524" lat="55.7524913" lon="37.6397264"/> + <node id="271892033" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:17Z" user="Scondo" uid="421524" lat="55.7522475" lon="37.6391447"/> + <node id="583193392" visible="true" version="2" changeset="4128036" timestamp="2010-03-14T18:31:07Z" user="Vovanium" uid="87682" lat="55.7507909" lon="37.6404902"/> + <node id="583193432" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7510964" lon="37.6397197"/> + <node id="583193426" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7510560" lon="37.6394035"/> + <node id="583193429" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7512865" lon="37.6396919"/> + <node id="583193395" visible="true" version="2" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7509787" lon="37.6401799"/> + <node id="583193415" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7510898" lon="37.6403571"/> + <node id="583193424" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7508581" lon="37.6399029"/> + <node id="583193422" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7509689" lon="37.6400415"/> + <node id="583193398" visible="true" version="2" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7514775" lon="37.6401937"/> + <node id="583193416" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7513532" lon="37.6405069"/> + <node id="583193431" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7512162" lon="37.6398695"/> + <node id="583193390" visible="true" version="2" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7507783" lon="37.6410989"/> + <node id="583193388" visible="true" version="2" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7509982" lon="37.6416194"/> + <node id="583193405" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7514149" lon="37.6406910"/> + <node id="583193408" visible="true" version="2" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7509930" lon="37.6412441"/> + <node id="583193410" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7509124" lon="37.6406648"/> + <node id="583193401" visible="true" version="2" changeset="4128036" timestamp="2010-03-14T18:31:09Z" user="Vovanium" uid="87682" lat="55.7516648" lon="37.6407506"/> + <node id="666179513" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7510740" lon="37.6401059"/> + <node id="666179517" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7511507" lon="37.6403206"/> + <node id="666179519" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7512863" lon="37.6403782"/> + <node id="666179521" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7512185" lon="37.6403206"/> + <node id="666179522" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7512214" lon="37.6400483"/> + <node id="666179524" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7513393" lon="37.6401111"/> + <node id="666179526" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7514337" lon="37.6402525"/> + <node id="666179528" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7507439" lon="37.6406349"/> + <node id="666179530" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7507291" lon="37.6407868"/> + <node id="666179531" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7507380" lon="37.6409544"/> + <node id="666179540" visible="true" version="1" changeset="4128036" timestamp="2010-03-14T18:30:48Z" user="Vovanium" uid="87682" lat="55.7508736" lon="37.6408287"/> + <node id="595699492" visible="true" version="2" changeset="4128036" timestamp="2010-03-14T18:31:08Z" user="Vovanium" uid="87682" lat="55.7508900" lon="37.6410015"/> + <node id="271892037" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:03Z" user="Scondo" uid="421524" lat="55.7519689" lon="37.6393462"/> + <node id="666179544" visible="true" version="2" changeset="8261156" timestamp="2011-05-27T10:18:03Z" user="Scondo" uid="421524" lat="55.7523858" lon="37.6394615"/> + <node id="271892040" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:09Z" user="Scondo" uid="421524" lat="55.7518044" lon="37.6401900"/> + <node id="271892039" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:11Z" user="Scondo" uid="421524" lat="55.7518997" lon="37.6400631"/> + <node id="271892031" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:23Z" user="Scondo" uid="421524" lat="55.7517772" lon="37.6406618"/> + <node id="271892036" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:23Z" user="Scondo" uid="421524" lat="55.7521424" lon="37.6397730"/> + <node id="271892035" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:25Z" user="Scondo" uid="421524" lat="55.7522520" lon="37.6396264"/> + <node id="666179542" visible="true" version="2" changeset="8261156" timestamp="2011-05-27T10:18:26Z" user="Scondo" uid="421524" lat="55.7523415" lon="37.6393631"/> + <node id="271892038" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:30Z" user="Scondo" uid="421524" lat="55.7517353" lon="37.6396389"/> + <node id="666179545" visible="true" version="2" changeset="8261156" timestamp="2011-05-27T10:18:30Z" user="Scondo" uid="421524" lat="55.7523947" lon="37.6392844"/> + <node id="271892041" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:34Z" user="Scondo" uid="421524" lat="55.7516804" lon="37.6398672"/> + <node id="666179548" visible="true" version="2" changeset="8261156" timestamp="2011-05-27T10:18:35Z" user="Scondo" uid="421524" lat="55.7524390" lon="37.6393828"/> + <node id="271892030" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:38Z" user="Scondo" uid="421524" lat="55.7515240" lon="37.6400640"/> + <node id="271892034" visible="true" version="4" changeset="8261156" timestamp="2011-05-27T10:18:40Z" user="Scondo" uid="421524" lat="55.7521203" lon="37.6393028"/> + <node id="2849850611" visible="true" version="2" changeset="33550372" timestamp="2015-08-24T15:55:36Z" user="vadp" uid="326091" lat="55.7507261" lon="37.6405934"/> + <node id="2849850614" visible="true" version="1" changeset="22264538" timestamp="2014-05-11T07:26:43Z" user="Vadim Zudkin" uid="177747" lat="55.7509233" lon="37.6401297"/> + <node id="3712207029" visible="true" version="1" changeset="33550372" timestamp="2015-08-24T15:55:35Z" user="vadp" uid="326091" lat="55.7510865" lon="37.6400013"/> + <node id="3712207030" visible="true" version="1" changeset="33550372" timestamp="2015-08-24T15:55:35Z" user="vadp" uid="326091" lat="55.7512462" lon="37.6399456"/> + <node id="3712207031" visible="true" version="2" changeset="33550412" timestamp="2015-08-24T15:57:10Z" user="vadp" uid="326091" lat="55.7514944" lon="37.6401534"/> + <node id="3712207032" visible="true" version="2" changeset="33550412" timestamp="2015-08-24T15:57:10Z" user="vadp" uid="326091" lat="55.7516969" lon="37.6407362"> + <tag k="access" v="private"/> + <tag k="barrier" v="gate"/> + </node> + <node id="3712207033" visible="true" version="2" changeset="33550412" timestamp="2015-08-24T15:57:10Z" user="vadp" uid="326091" lat="55.7517316" lon="37.6408217"/> + <node id="3712207034" visible="true" version="2" changeset="33550412" timestamp="2015-08-24T15:57:10Z" user="vadp" uid="326091" lat="55.7517602" lon="37.6409066"/> + <node id="2849850613" visible="true" version="3" changeset="33551686" timestamp="2015-08-24T16:50:21Z" user="vadp" uid="326091" lat="55.7507965" lon="37.6399611"/> + <node id="338464706" visible="true" version="3" changeset="33551686" timestamp="2015-08-24T16:50:21Z" user="vadp" uid="326091" lat="55.7510322" lon="37.6393637"/> + <node id="338464708" visible="true" version="7" changeset="33551686" timestamp="2015-08-24T16:50:21Z" user="vadp" uid="326091" lat="55.7515407" lon="37.6383137"/> + <node id="3755931947" visible="true" version="1" changeset="34206452" timestamp="2015-09-23T13:58:11Z" user="trolleway" uid="397326" lat="55.7517090" lon="37.6407565"/> + <way id="25009838" visible="true" version="14" changeset="28090002" timestamp="2015-01-12T16:15:21Z" user="midrug" uid="2417727"> + <nd ref="271892030"/> + <nd ref="271892031"/> + <nd ref="271892032"/> + <nd ref="666179544"/> + <nd ref="666179548"/> + <nd ref="666179545"/> + <nd ref="666179542"/> + <nd ref="271892033"/> + <nd ref="271892034"/> + <nd ref="271892035"/> + <nd ref="271892036"/> + <nd ref="271892037"/> + <nd ref="271892038"/> + <nd ref="271892039"/> + <nd ref="271892040"/> + <nd ref="271892041"/> + <nd ref="271892030"/> + <tag k="addr:housenumber" v="12-14"/> + <tag k="addr:street" v="улица Солянка"/> + <tag k="building" v="yes"/> + <tag k="building:colour" v="lightpink"/> + <tag k="building:levels" v="3"/> + <tag k="description:en" v="Housed the Board of Trustees, a public institution of the Russian Empire until 1917"/> + <tag k="end_date" v="1826"/> + <tag k="name" v="Опекунский совет"/> + <tag k="name:de" v="Kuratorium"/> + <tag k="name:en" v="Board of Trustees Building"/> + <tag k="ref" v="7710784000"/> + <tag k="roof:material" v="metal"/> + <tag k="source:description:en" v="wikipedia:ru"/> + <tag k="start_date" v="1823"/> + <tag k="tourism" v="attraction"/> + <tag k="wikipedia" v="ru:Опекунский совет (Москва)"/> + </way> + <way id="45814282" visible="true" version="3" changeset="4128036" timestamp="2010-03-14T18:31:24Z" user="Vovanium" uid="87682"> + <nd ref="583193405"/> + <nd ref="583193408"/> + <nd ref="595699492"/> + <nd ref="666179540"/> + <nd ref="583193410"/> + <nd ref="583193415"/> + <nd ref="666179517"/> + <nd ref="666179521"/> + <nd ref="666179519"/> + <nd ref="583193416"/> + <nd ref="583193405"/> + </way> + <way id="109538181" visible="true" version="7" changeset="34206452" timestamp="2015-09-23T13:58:11Z" user="trolleway" uid="397326"> + <nd ref="338464708"/> + <nd ref="338464706"/> + <nd ref="2849850613"/> + <nd ref="2849850614"/> + <nd ref="3712207029"/> + <nd ref="3712207030"/> + <nd ref="3712207031"/> + <nd ref="3712207032"/> + <nd ref="3755931947"/> + <nd ref="3712207033"/> + <nd ref="3712207034"/> + <tag k="highway" v="service"/> + </way> + <way id="45814281" visible="true" version="6" changeset="9583527" timestamp="2011-10-17T16:38:55Z" user="luch86" uid="266092"> + <nd ref="583193388"/> + <nd ref="583193390"/> + <nd ref="666179531"/> + <nd ref="666179530"/> + <nd ref="666179528"/> + <nd ref="583193392"/> + <nd ref="583193395"/> + <nd ref="666179513"/> + <nd ref="666179522"/> + <nd ref="666179524"/> + <nd ref="666179526"/> + <nd ref="583193398"/> + <nd ref="583193401"/> + <nd ref="583193388"/> + </way> + <way id="45814283" visible="true" version="3" changeset="28090002" timestamp="2015-01-12T16:15:23Z" user="midrug" uid="2417727"> + <nd ref="583193422"/> + <nd ref="583193424"/> + <nd ref="583193426"/> + <nd ref="583193429"/> + <nd ref="583193431"/> + <nd ref="583193432"/> + <nd ref="583193422"/> + <tag k="addr:street" v="улица Солянка"/> + <tag k="building" v="yes"/> + <tag k="building:colour" v="goldenrod"/> + <tag k="building:levels" v="2"/> + <tag k="roof:colour" v="black"/> + <tag k="roof:material" v="tar_paper"/> + </way> + <way id="367274913" visible="true" version="2" changeset="33550484" timestamp="2015-08-24T16:00:25Z" user="vadp" uid="326091"> + <nd ref="2849850614"/> + <nd ref="2849850611"/> + <tag k="highway" v="service"/> + </way> + <relation id="365808" visible="true" version="6" changeset="28090002" timestamp="2015-01-12T16:15:14Z" user="midrug" uid="2417727"> + <member type="way" ref="45814281" role="outer"/> + <member type="way" ref="45814282" role="inner"/> + <tag k="addr:housenumber" v="14/2"/> + <tag k="addr:street" v="улица Солянка"/> + <tag k="building" v="yes"/> + <tag k="building:colour" v="gold"/> + <tag k="building:levels" v="2"/> + <tag k="roof:material" v="metal"/> + <tag k="type" v="multipolygon"/> + </relation> +</osm> +)SEP"; +} // namespace + +UNIT_TEST(GetBestOsmNode_Test) +{ + { + pugi::xml_document osmResponse; + TEST(osmResponse.load_buffer(osmRawResponseNode, ::strlen(osmRawResponseNode)), ()); + + auto const bestNode = osm::GetBestOsmNode(osmResponse, ms::LatLon(53.8977398, 27.5579251)); + TEST_EQUAL(editor::XMLFeature(bestNode).GetName(), + "Главное управление капитального строительства", ()); + } + { + pugi::xml_document osmResponse; + TEST(osmResponse.load_buffer(osmRawResponseNode, ::strlen(osmRawResponseNode)), ()); + + auto const bestNode = osm::GetBestOsmNode(osmResponse, ms::LatLon(53.8978398, 27.5579251)); + TEST(bestNode, ()); + } +} + UNIT_TEST(GetBestOsmWay_Test) { { @@ -99,7 +297,7 @@ UNIT_TEST(GetBestOsmWay_Test) MercatorBounds::FromLatLon(53.8976041, 27.557518), }; - auto const bestWay = osm::GetBestOsmWay(osmResponse, geometry); + auto const bestWay = osm::GetBestOsmWayOrRelation(osmResponse, geometry); TEST_EQUAL(editor::XMLFeature(bestWay).GetName(), "Беллесбумпром", ()); } { @@ -115,26 +313,42 @@ UNIT_TEST(GetBestOsmWay_Test) MercatorBounds::FromLatLon(53.8976041, 27.557318), // diff }; - auto const bestWay = osm::GetBestOsmWay(osmResponse, geometry); + auto const bestWay = osm::GetBestOsmWayOrRelation(osmResponse, geometry); TEST(!bestWay, ()); } } -UNIT_TEST(GetBestOsmNode_Test) +UNIT_TEST(GetBestOsmRealtion_Test) { - { - pugi::xml_document osmResponse; - TEST(osmResponse.load_buffer(osmRawResponseNode, ::strlen(osmRawResponseNode)), ()); + pugi::xml_document osmResponse; + TEST(osmResponse.load_buffer(osmRawResponseRelation, ::strlen(osmRawResponseRelation)), ()); + vector<m2::PointD> const geometry = { + {37.6400469, 67.4549381}, + {37.6401059, 67.4546779}, + {37.6401113, 67.4551473}, + {37.640181, 67.4545089}, + {37.6401944, 67.455394}, + {37.6402534, 67.4553162}, + {37.6403205, 67.454812}, + {37.6403205, 67.4549327}, + {37.640358, 67.4547047}, + {37.6403768, 67.4550534}, + {37.6404895, 67.4541736}, + {37.6405056, 67.4551741}, + {37.6406343, 67.4540905}, + {37.6406638, 67.4543909}, + {37.6406906, 67.4552814}, + {37.6407496, 67.4557266}, + {37.6407496, 67.45572}, + {37.6407872, 67.4540636}, + {37.6408274, 67.4543211}, + {37.6409535, 67.4540797}, + {37.6410018, 67.4543506}, + {37.6410983, 67.4541522}, + {37.6412432, 67.454533}, + {37.6416187, 67.4545411} + }; - auto const bestNode = osm::GetBestOsmNode(osmResponse, ms::LatLon(53.8977398, 27.5579251)); - TEST_EQUAL(editor::XMLFeature(bestNode).GetName(), - "Главное управление капитального строительства", ()); - } - { - pugi::xml_document osmResponse; - TEST(osmResponse.load_buffer(osmRawResponseNode, ::strlen(osmRawResponseNode)), ()); - - auto const bestNode = osm::GetBestOsmNode(osmResponse, ms::LatLon(53.8978398, 27.5579251)); - TEST(bestNode, ()); - } + auto const bestWay = osm::GetBestOsmWayOrRelation(osmResponse, geometry); + TEST_EQUAL(bestWay.attribute("id").value(), string("365808"), ()); } diff --git a/editor/osm_feature_matcher.cpp b/editor/osm_feature_matcher.cpp index 1605a85729..fac7dc7305 100644 --- a/editor/osm_feature_matcher.cpp +++ b/editor/osm_feature_matcher.cpp @@ -1,13 +1,16 @@ #include "editor/osm_feature_matcher.hpp" #include "base/logging.hpp" +#include "base/stl_helpers.hpp" #include "std/algorithm.hpp" using editor::XMLFeature; -namespace +namespace osm { +using editor::XMLFeature; + constexpr double kPointDiffEps = MercatorBounds::GetCellID2PointAbsEpsilon(); bool PointsEqual(m2::PointD const & a, m2::PointD const & b) @@ -38,6 +41,22 @@ void ForEachWaysNode(pugi::xml_document const & osmResponse, pugi::xml_node cons } } +template <typename TFunc> +void ForEachRelationsNode(pugi::xml_document const & osmResponse, pugi::xml_node const & relation, + TFunc && func) +{ + for (auto const xNodeRef : relation.select_nodes("member[@type='way']/@ref")) + { + string const wayRef = xNodeRef.attribute().value(); + auto const xpath = "osm/way[@id='" + wayRef + "']"; + auto const way = osmResponse.select_node(xpath.data()).node(); + // Some ways can be missed from relation. + if (!way) + continue; + ForEachWaysNode(osmResponse, way, forward<TFunc>(func)); + } +} + vector<m2::PointD> GetWaysGeometry(pugi::xml_document const & osmResponse, pugi::xml_node const & way) { @@ -49,54 +68,77 @@ vector<m2::PointD> GetWaysGeometry(pugi::xml_document const & osmResponse, return result; } +vector<m2::PointD> GetRelationsGeometry(pugi::xml_document const & osmResponse, + pugi::xml_node const & relation) +{ + vector<m2::PointD> result; + ForEachRelationsNode(osmResponse, relation, [&result](XMLFeature const & xmlFt) + { + result.push_back(xmlFt.GetMercatorCenter()); + }); + return result; +} + +// TODO(mgsergio): XMLFeature should have GetGeometry method. +vector<m2::PointD> GetWaysOrRelationsGeometry(pugi::xml_document const & osmResponse, + pugi::xml_node const & wayOrRelation) +{ + if (strcmp(wayOrRelation.name(), "way") == 0) + return GetWaysGeometry(osmResponse, wayOrRelation); + return GetRelationsGeometry(osmResponse, wayOrRelation); +} + /// @returns value form [-0.5, 0.5]. Negative values are used as penalty, /// positive as score. -double ScoreGeometry(pugi::xml_document const & osmResponse, pugi::xml_node const & way, - vector<m2::PointD> geometry) +/// @param osmResponse - nodes, ways and relations from osm +/// @param wayOrRelation - either way or relation to be compared agains ourGeometry +/// @param outGeometry - geometry of a FeatureType (ourGeometry must be sort-uniqued) +double ScoreGeometry(pugi::xml_document const & osmResponse, + pugi::xml_node const & wayOrRelation, vector<m2::PointD> ourGeometry) { + ASSERT(!ourGeometry.empty(), ("Our geometry cannot be empty")); int matched = 0; - auto wayGeometry = GetWaysGeometry(osmResponse, way); + auto theirGeometry = GetWaysOrRelationsGeometry(osmResponse, wayOrRelation); + + if (theirGeometry.empty()) + return -1; - sort(begin(wayGeometry), end(wayGeometry)); - sort(begin(geometry), end(geometry)); + my::SortUnique(theirGeometry); - auto it1 = begin(geometry); - auto it2 = begin(wayGeometry); + auto ourIt = begin(ourGeometry); + auto theirIt = begin(theirGeometry); - while (it1 != end(geometry) && it2 != end(wayGeometry)) + while (ourIt != end(ourGeometry) && theirIt != end(theirGeometry)) { - if (PointsEqual(*it1, *it2)) + if (PointsEqual(*ourIt, *theirIt)) { ++matched; - ++it1; - ++it2; + ++ourIt; + ++theirIt; } - else if (*it1 < *it2) + else if (*ourIt < *theirIt) { - ++it1; + ++ourIt; } else { - ++it2; + ++theirIt; } } - auto const wayScore = static_cast<double>(matched) / wayGeometry.size() - 0.5; - auto const geomScore = static_cast<double>(matched) / geometry.size() - 0.5; + auto const wayScore = static_cast<double>(matched) / theirGeometry.size() - 0.5; + auto const geomScore = static_cast<double>(matched) / ourGeometry.size() - 0.5; auto const result = wayScore <= 0 || geomScore <= 0 ? -1 : 2 / (1 / wayScore + 1 / geomScore); - LOG(LDEBUG, ("Osm score:", wayScore, "our feature score:", geomScore, - "Total score", result)); + LOG(LDEBUG, ("Type:", wayOrRelation.name(), "Osm score:", + wayScore, "our feature score:", geomScore, "Total score", result)); return result; } -} // namespace -namespace osm -{ pugi::xml_node GetBestOsmNode(pugi::xml_document const & osmResponse, ms::LatLon const & latLon) { double bestScore = -1; @@ -132,23 +174,23 @@ pugi::xml_node GetBestOsmNode(pugi::xml_document const & osmResponse, ms::LatLon return bestMatchNode; } -pugi::xml_node GetBestOsmWay(pugi::xml_document const & osmResponse, - vector<m2::PointD> const & geometry) +pugi::xml_node GetBestOsmWayOrRelation(pugi::xml_document const & osmResponse, + vector<m2::PointD> const & geometry) { double bestScore = -1; pugi::xml_node bestMatchWay; - // TODO(mgsergio): Handle relations as well. Put try_later=version status to edits.xml. - for (auto const & xWay : osmResponse.select_nodes("osm/way")) + auto const xpath = "osm/way|osm/relation[tag[@k='type' and @v='multipolygon']]"; + for (auto const & xWayOrRelation : osmResponse.select_nodes(xpath)) { - double const nodeScore = ScoreGeometry(osmResponse, xWay.node(), geometry); + double const nodeScore = ScoreGeometry(osmResponse, xWayOrRelation.node(), geometry); if (nodeScore < 0) continue; if (bestScore < nodeScore) { bestScore = nodeScore; - bestMatchWay = xWay.node(); + bestMatchWay = xWayOrRelation.node(); } } diff --git a/editor/osm_feature_matcher.hpp b/editor/osm_feature_matcher.hpp index 0c1f05e7bb..2d3947317f 100644 --- a/editor/osm_feature_matcher.hpp +++ b/editor/osm_feature_matcher.hpp @@ -12,6 +12,6 @@ namespace osm /// @returns closest to the latLon node from osm or empty node if none is close enough. pugi::xml_node GetBestOsmNode(pugi::xml_document const & osmResponse, ms::LatLon const & latLon); /// @returns a way from osm with similar geometry or empy node if can't find such way. -pugi::xml_node GetBestOsmWay(pugi::xml_document const & osmResponse, - vector<m2::PointD> const & geometry); +pugi::xml_node GetBestOsmWayOrRelation(pugi::xml_document const & osmResponse, + vector<m2::PointD> const & geometry); } // namespace osm |