From 490416568e671c34b543a341600c650f7558fcef Mon Sep 17 00:00:00 2001 From: Arsentiy Milchakov Date: Tue, 23 Jan 2018 14:20:03 +0300 Subject: [PATCH] [editor] Support migration for old versions of edits. --- editor/feature_matcher.cpp | 37 +++++++++++++++++++++++++++++++++---- editor/feature_matcher.hpp | 10 +++++++++- indexer/edits_migration.cpp | 19 +++++++++++++++---- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/editor/feature_matcher.cpp b/editor/feature_matcher.cpp index ca15f7093f..54945b90cf 100644 --- a/editor/feature_matcher.cpp +++ b/editor/feature_matcher.cpp @@ -2,6 +2,7 @@ #include "base/logging.hpp" #include "base/stl_helpers.hpp" +#include "base/stl_iterator.hpp" #include "std/algorithm.hpp" #include "std/function.hpp" @@ -109,8 +110,9 @@ double MatchByGeometry(LGeometry const & lhs, RGeometry const & rhs) MultiPolygon TrianglesToPolygon(vector const & points) { size_t const kTriangleSize = 3; - CHECK_EQUAL(points.size() % kTriangleSize, 0, ()); - CHECK(!points.empty(), ()); + if (points.size() % kTriangleSize != 0 || points.empty()) + MYTHROW(matcher::NotAPolygonException, ("Count of points must be multiple of", kTriangleSize)); + vector polygons; for (size_t i = 0; i < points.size(); i += kTriangleSize) { @@ -121,11 +123,14 @@ MultiPolygon TrianglesToPolygon(vector const & points) for (size_t j = i; j < i + kTriangleSize; ++j) outer.push_back(PointXY(points[j].x, points[j].y)); bg::correct(p); - ASSERT(bg::is_valid(polygon), ()); + if (!bg::is_valid(polygon)) + MYTHROW(matcher::NotAPolygonException, ("The triangle is not valid")); polygons.push_back(polygon); } - CHECK(!polygons.empty(), ()); + if (polygons.empty()) + return {}; + auto & result = polygons[0]; for (size_t i = 1; i < polygons.size(); ++i) { @@ -345,4 +350,28 @@ double ScoreTriangulatedGeometries(vector const & lhs, vector const & lhs, + vector const & rhs) +{ + // The default comparison operator used in sort above (cmp1) and one that is + // used in set_itersection (cmp2) are compatible in that sence that + // cmp2(a, b) :- cmp1(a, b) and + // cmp1(a, b) :- cmp2(a, b) || a almost equal b. + // You can think of cmp2 as !(a >= b). + // But cmp2 is not transitive: + // i.e. !cmp(a, b) && !cmp(b, c) does NOT implies !cmp(a, c), + // |a, b| < eps, |b, c| < eps. + // This could lead to unexpected results in set_itersection (with greedy implementation), + // but we assume such situation is very unlikely. + auto const matched = set_intersection(begin(lhs), end(lhs), + begin(rhs), end(rhs), + CounterIterator(), + [](m2::PointD const & p1, m2::PointD const & p2) + { + return p1 < p2 && !p1.EqualDxDy(p2, 1e-7); + }).GetCount(); + + return static_cast(matched) / lhs.size(); +} } // namespace matcher diff --git a/editor/feature_matcher.hpp b/editor/feature_matcher.hpp index dd697b944d..e9f6180e10 100644 --- a/editor/feature_matcher.hpp +++ b/editor/feature_matcher.hpp @@ -5,16 +5,24 @@ #include "geometry/mercator.hpp" #include "geometry/point2d.hpp" +#include "base/exception.hpp" + #include "std/vector.hpp" namespace matcher { +DECLARE_EXCEPTION(NotAPolygonException, RootException); /// 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 empty node if can't find such way. +/// Throws NotAPolygon exception when |geometry| is not convertible to a polygon. pugi::xml_node GetBestOsmWayOrRelation(pugi::xml_document const & osmResponse, vector const & geometry); /// Returns value form [-1, 1]. Negative values are used as penalty, positive as score. -/// |lhs| and |rhs| - triangulated polygons; +/// |lhs| and |rhs| - triangulated polygons. +/// Throws NotAPolygon exception when lhs or rhs is not convertible to a polygon. double ScoreTriangulatedGeometries(vector const & lhs, vector const & rhs); +/// Deprecated, use ScoreTriangulatedGeometries instead. +double ScoreTriangulatedGeometriesByPoints(vector const & lhs, + vector const & rhs); } // namespace osm diff --git a/indexer/edits_migration.cpp b/indexer/edits_migration.cpp index 5562651254..7b533cd3a0 100644 --- a/indexer/edits_migration.cpp +++ b/indexer/edits_migration.cpp @@ -7,7 +7,6 @@ #include "editor/feature_matcher.hpp" #include "base/logging.hpp" -#include "base/stl_iterator.hpp" #include "std/algorithm.hpp" #include "std/unique_ptr.hpp" @@ -45,7 +44,7 @@ FeatureID MigrateNodeFeatureIndex(osm::Editor::ForEachFeaturesNearByFn & forEach return feature->GetID(); } -FeatureID MigrateWayorRelatonFeatureIndex( +FeatureID MigrateWayOrRelatonFeatureIndex( osm::Editor::ForEachFeaturesNearByFn & forEach, XMLFeature const & xml, osm::Editor::FeatureStatus const /* Unused for now (we don't create/delete area features)*/, TGenerateIDFn const & /*Unused for the same reason*/) @@ -69,7 +68,19 @@ FeatureID MigrateWayorRelatonFeatureIndex( ++count; auto ftGeometry = ft.GetTriangesAsPoints(FeatureType::BEST_GEOMETRY); - auto const score = matcher::ScoreTriangulatedGeometries(geometry, ftGeometry); + double score = 0.0; + try + { + score = matcher::ScoreTriangulatedGeometries(geometry, ftGeometry); + } + catch (matcher::NotAPolygonException & ex) + { + // Support migration for old application versions. + // TODO(a): To remove it after some time. + my::SortUnique(geometry); + my::SortUnique(ftGeometry); + score = matcher::ScoreTriangulatedGeometriesByPoints(geometry, ftGeometry); + } if (score > bestScore) { @@ -103,7 +114,7 @@ FeatureID MigrateFeatureIndex(osm::Editor::ForEachFeaturesNearByFn & forEach, return MigrateNodeFeatureIndex(forEach, xml, featureStatus, generateID); case XMLFeature::Type::Way: case XMLFeature::Type::Relation: - return MigrateWayorRelatonFeatureIndex(forEach, xml, featureStatus, generateID); + return MigrateWayOrRelatonFeatureIndex(forEach, xml, featureStatus, generateID); } } } // namespace editor