Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor/xml_feature.cpp15
-rw-r--r--editor/xml_feature.hpp24
-rw-r--r--geometry/algorithm.cpp81
-rw-r--r--geometry/algorithm.hpp59
-rw-r--r--geometry/geometry.pro2
-rw-r--r--geometry/geometry_tests/algorithm_test.cpp166
-rw-r--r--geometry/geometry_tests/geometry_tests.pro1
-rw-r--r--indexer/edits_migration.cpp135
-rw-r--r--indexer/edits_migration.hpp18
-rw-r--r--indexer/feature.cpp2
-rw-r--r--indexer/feature.hpp7
-rw-r--r--indexer/feature_algo.cpp102
-rw-r--r--indexer/indexer.pro2
-rw-r--r--indexer/osm_editor.cpp72
-rw-r--r--indexer/osm_editor.hpp12
-rw-r--r--map/framework.cpp1
16 files changed, 537 insertions, 162 deletions
diff --git a/editor/xml_feature.cpp b/editor/xml_feature.cpp
index b5729a98cb..27b7750781 100644
--- a/editor/xml_feature.cpp
+++ b/editor/xml_feature.cpp
@@ -10,7 +10,6 @@
namespace
{
-constexpr int const kLatLonTolerance = 7;
constexpr char const * kTimestamp = "timestamp";
constexpr char const * kIndex = "mwm_file_index";
constexpr char const * kUploadTimestamp = "upload_timestamp";
@@ -193,20 +192,6 @@ XMLFeature::TMercatorGeometry XMLFeature::GetGeometry() const
return geometry;
}
-/// Geometry points are now stored in <nd x="..." y="..." /> nodes like in osm <way>.
-/// But they are not the same as osm's. I.e. osm's one stores reference to a <node>
-/// 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;
diff --git a/editor/xml_feature.hpp b/editor/xml_feature.hpp
index 83578a89e9..fd6d167355 100644
--- a/editor/xml_feature.hpp
+++ b/editor/xml_feature.hpp
@@ -29,6 +29,9 @@ class XMLFeature
static char const * const kDefaultLang;
public:
+ // Used in point to string serialization.
+ static constexpr int kLatLonTolerance = 7;
+
enum class Type
{
Node,
@@ -67,7 +70,26 @@ public:
/// Sets geometry in mercator to match against FeatureType's geometry in mwm
/// when megrating to a new mwm build.
- void SetGeometry(TMercatorGeometry const & geometry);
+ /// Geometry points are now stored in <nd x="..." y="..." /> nodes like in osm <way>.
+ /// But they are not the same as osm's. I.e. osm's one stores reference to a <node>
+ /// with it's own data and lat, lon. Here we store only cooridanes in mercator.
+ template <typename TIterator>
+ void SetGeometry(TIterator begin, TIterator end)
+ {
+ ASSERT_EQUAL(GetType(), Type::Way, ("Only ways have geometry"));
+ for (; begin != end; ++begin)
+ {
+ auto nd = GetRootNode().append_child("nd");
+ nd.append_attribute("x") = strings::to_string_dac(begin->x, kLatLonTolerance).data();
+ nd.append_attribute("y") = strings::to_string_dac(begin->y, kLatLonTolerance).data();
+ }
+ }
+
+ template <typename TCollection>
+ void SetGeometry(TCollection const & geometry)
+ {
+ SetGeometry(begin(geometry), end(geometry));
+ }
string GetName(string const & lang) const;
string GetName(uint8_t const langCode = StringUtf8Multilang::DEFAULT_CODE) const;
diff --git a/geometry/algorithm.cpp b/geometry/algorithm.cpp
new file mode 100644
index 0000000000..76ebe95f09
--- /dev/null
+++ b/geometry/algorithm.cpp
@@ -0,0 +1,81 @@
+#include "geometry/algorithm.hpp"
+#include "geometry/triangle2d.hpp"
+
+#include "base/logging.hpp"
+
+namespace m2
+{
+// CalculatePolyLineCenter -----------------------------------------------------------------------------
+
+void CalculatePolyLineCenter::operator()(m2::PointD const & pt)
+{
+ m_length += (m_poly.empty() ? 0.0 : m_poly.back().m_p.Length(pt));
+ m_poly.emplace_back(pt, m_length);
+}
+
+PointD CalculatePolyLineCenter::GetCenter() const
+{
+ using TIter = vector<Value>::const_iterator;
+
+ double const l = m_length / 2.0;
+
+ TIter e = lower_bound(m_poly.begin(), m_poly.end(), Value(m2::PointD(0, 0), l));
+ if (e == m_poly.begin())
+ {
+ /// @todo It's very strange, but we have linear objects with zero length.
+ LOG(LWARNING, ("Zero length linear object"));
+ return e->m_p;
+ }
+
+ TIter b = e - 1;
+
+ double const f = (l - b->m_len) / (e->m_len - b->m_len);
+
+ // For safety reasons (floating point calculations) do comparison instead of ASSERT.
+ if (0.0 <= f && f <= 1.0)
+ return (b->m_p * (1 - f) + e->m_p * f);
+ else
+ return ((b->m_p + e->m_p) / 2.0);
+}
+
+// CalculatePointOnSurface -------------------------------------------------------------------------
+
+CalculatePointOnSurface::CalculatePointOnSurface(m2::RectD const & rect)
+ : m_rectCenter(rect.Center())
+ , m_center(m_rectCenter)
+ , m_squareDistanceToApproximate(numeric_limits<double>::max())
+{
+}
+
+void CalculatePointOnSurface::operator()(PointD const & p1, PointD const & p2, PointD const & p3)
+{
+ if (m_squareDistanceToApproximate == 0.0)
+ return;
+ if (m2::IsPointInsideTriangle(m_rectCenter, p1, p2, p3))
+ {
+ m_center = m_rectCenter;
+ m_squareDistanceToApproximate = 0.0;
+ return;
+ }
+ PointD triangleCenter(p1);
+ triangleCenter += p2;
+ triangleCenter += p3;
+ triangleCenter = triangleCenter / 3.0;
+
+ double triangleDistance = m_rectCenter.SquareLength(triangleCenter);
+ if (triangleDistance <= m_squareDistanceToApproximate)
+ {
+ m_center = triangleCenter;
+ m_squareDistanceToApproximate = triangleDistance;
+ }
+}
+
+// CalculateBoundingBox ----------------------------------------------------------------------------
+
+void CalculateBoundingBox::operator()(PointD const & p)
+{
+ // Works just fine. If you don't belive me, see geometry/rect2d.hpp.
+ // Pay attention to MakeEmpty and Add functions.
+ m_boundingBox.Add(p);
+}
+} // namespace m2
diff --git a/geometry/algorithm.hpp b/geometry/algorithm.hpp
new file mode 100644
index 0000000000..6513bd6885
--- /dev/null
+++ b/geometry/algorithm.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "geometry/point2d.hpp"
+#include "geometry/rect2d.hpp"
+
+#include "std/vector.hpp"
+
+namespace m2
+{
+// This class is used to calculate a point on a polyline
+// such as that the paths (not the distance) from that point
+// to both ends of a polyline have the same length.
+class CalculatePolyLineCenter
+{
+public:
+ CalculatePolyLineCenter() : m_length(0.0) {}
+ void operator()(PointD const & pt);
+
+ PointD GetCenter() const;
+
+private:
+ struct Value
+ {
+ Value(PointD const & p, double l) : m_p(p), m_len(l) {}
+ bool operator<(Value const & r) const { return (m_len < r.m_len); }
+ PointD m_p;
+ double m_len;
+ };
+
+ vector<Value> m_poly;
+ double m_length;
+};
+
+// This class is used to calculate a point such as that
+// it lays on the figure triangle that is the closest to
+// figure bounding box center.
+class CalculatePointOnSurface
+{
+public:
+ CalculatePointOnSurface(RectD const & rect);
+
+ void operator()(PointD const & p1, PointD const & p2, PointD const & p3);
+ PointD GetCenter() const { return m_center; }
+private:
+ PointD m_rectCenter;
+ PointD m_center;
+ double m_squareDistanceToApproximate;
+};
+
+// Calculates the smallest rect that includes given geometry.
+class CalculateBoundingBox
+{
+public:
+ void operator()(PointD const & p);
+ RectD GetBoundingBox() const { return m_boundingBox; }
+private:
+ RectD m_boundingBox;
+};
+} // namespace m2
diff --git a/geometry/geometry.pro b/geometry/geometry.pro
index 9f297dacbf..ff07f193ff 100644
--- a/geometry/geometry.pro
+++ b/geometry/geometry.pro
@@ -9,6 +9,7 @@ ROOT_DIR = ..
include($$ROOT_DIR/common.pri)
SOURCES += \
+ algorithm.cpp \
angles.cpp \
distance_on_sphere.cpp \
latlon.cpp \
@@ -22,6 +23,7 @@ SOURCES += \
triangle2d.cpp \
HEADERS += \
+ algorithm.hpp \
angles.hpp \
any_rect2d.hpp \
avg_vector.hpp \
diff --git a/geometry/geometry_tests/algorithm_test.cpp b/geometry/geometry_tests/algorithm_test.cpp
new file mode 100644
index 0000000000..3f349f9791
--- /dev/null
+++ b/geometry/geometry_tests/algorithm_test.cpp
@@ -0,0 +1,166 @@
+#include "testing/testing.hpp"
+
+#include "geometry/algorithm.hpp"
+
+#include "std/vector.hpp"
+
+using m2::PointD;
+using m2::RectD;
+using m2::CalculatePolyLineCenter;
+using m2::CalculatePointOnSurface;
+using m2::CalculateBoundingBox;
+
+namespace
+{
+PointD GetPolyLineCenter(vector<PointD> const & points)
+{
+ CalculatePolyLineCenter doCalc;
+ for (auto const & p : points)
+ doCalc(p);
+ return doCalc.GetCenter();
+}
+
+RectD GetBoundingBox(vector<PointD> const & points)
+{
+ CalculateBoundingBox doCalc;
+ for (auto const p : points)
+ doCalc(p);
+ return doCalc.GetBoundingBox();
+}
+
+PointD GetPointOnSurface(vector<PointD> const & points)
+{
+ ASSERT(!points.empty() && points.size() % 3 == 0, ());
+ CalculatePointOnSurface doCalc(GetBoundingBox(points));
+ for (auto i = 0; i < points.size() - 3; i += 3)
+ doCalc(points[i], points[i + 1], points[i + 2]);
+ return doCalc.GetCenter();
+
+}
+} // namespace
+
+UNIT_TEST(CalculatePolyLineCenter)
+{
+ {
+ vector<PointD> const points {
+ {0, 0},
+ {1, 1},
+ {2, 2}
+ };
+ TEST_EQUAL(GetPolyLineCenter(points), PointD(1, 1), ());
+ }
+ {
+ vector<PointD> const points {
+ {0, 2},
+ {1, 1},
+ {2, 2}
+ };
+ TEST_EQUAL(GetPolyLineCenter(points), PointD(1, 1), ());
+ }
+ {
+ vector<PointD> const points {
+ {1, 1},
+ {2, 2},
+ {4, 4},
+ };
+ TEST_EQUAL(GetPolyLineCenter(points), PointD(2.5, 2.5), ());
+ }
+ {
+ vector<PointD> const points {
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0}
+ };
+ // Also logs a warning message.
+ TEST_EQUAL(GetPolyLineCenter(points), PointD(0, 0), ());
+ }
+ {
+ vector<PointD> const points {
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0.000001},
+ {0, 0.000001},
+ {0, 0.000001},
+ {0, 0.000001}
+ };
+ // Also logs a warning message.
+ TEST(GetPolyLineCenter(points).EqualDxDy(PointD(0, .0000005), 1e-7), ());
+ }
+}
+
+UNIT_TEST(CalculateCenter)
+{
+ {
+ vector<PointD> const points {
+ {2, 2}
+ };
+ TEST_EQUAL(GetBoundingBox(points), RectD({2, 2}, {2, 2}), ());
+ }
+ {
+ vector<PointD> const points {
+ {0, 0},
+ {1, 1},
+ {2, 2}
+ };
+ TEST_EQUAL(GetBoundingBox(points), RectD({0, 0}, {2, 2}), ());
+ }
+ {
+ vector<PointD> const points {
+ {0, 2},
+ {1, 1},
+ {2, 2},
+ {1, 2},
+ {2, 2},
+ {2, 0}
+ };
+ TEST_EQUAL(GetBoundingBox(points), RectD({0, 0}, {2, 2}), ());
+ }
+}
+
+UNIT_TEST(CalculatePointOnSurface)
+{
+ {
+ vector<PointD> const points {
+ {0, 0},
+ {1, 1},
+ {2, 2}
+ };
+ TEST_EQUAL(GetPointOnSurface(points), PointD(1, 1), ());
+ }
+ {
+ vector<PointD> const points {
+ {0, 2},
+ {1, 1},
+ {2, 2},
+ {1, 2},
+ {2, 2},
+ {2, 0}
+ };
+ TEST_EQUAL(GetPointOnSurface(points), PointD(1, 1), ());
+ }
+ {
+ vector<PointD> const points {
+ {0, 0}, {1, 1}, {2, 0},
+ {1, 1}, {2, 0}, {3, 1},
+ {2, 0}, {3, 1}, {4, 0},
+
+ {4, 0}, {3, 1}, {4, 2},
+ {3, 1}, {4, 2}, {3, 3},
+ {4, 2}, {3, 3}, {4, 4},
+
+ {3, 3}, {4, 4}, {2, 4},
+ {3, 3}, {2, 4}, {1, 3},
+ {1, 3}, {2, 4}, {0, 4},
+
+ {0, 4}, {1, 3}, {0, 2},
+ {1, 3}, {0, 2}, {1, 1}, // Center of this triangle is used as a result.
+ {0, 2}, {1, 1}, {0, 0},
+ };
+ TEST_EQUAL(GetPointOnSurface(points), PointD(2.0 / 3.0, 2), ());
+ }
+}
diff --git a/geometry/geometry_tests/geometry_tests.pro b/geometry/geometry_tests/geometry_tests.pro
index e9c04c136c..050968f793 100644
--- a/geometry/geometry_tests/geometry_tests.pro
+++ b/geometry/geometry_tests/geometry_tests.pro
@@ -19,6 +19,7 @@ HEADERS += \
SOURCES += \
../../testing/testingmain.cpp \
+ algorithm_test.cpp \
angle_test.cpp \
anyrect_test.cpp \
cellid_test.cpp \
diff --git a/indexer/edits_migration.cpp b/indexer/edits_migration.cpp
new file mode 100644
index 0000000000..92beaff9f0
--- /dev/null
+++ b/indexer/edits_migration.cpp
@@ -0,0 +1,135 @@
+#include "geometry/algorithm.hpp"
+#include "geometry/mercator.hpp"
+
+#include "indexer/edits_migration.hpp"
+#include "indexer/feature.hpp"
+
+#include "base/logging.hpp"
+
+#include "std/algorithm.hpp"
+#include "std/unique_ptr.hpp"
+
+namespace
+{
+m2::PointD CalculateCenter(vector<m2::PointD> const & geometry)
+{
+ ASSERT(!geometry.empty() && geometry.size() % 3 == 0,
+ ("Invalid geometry should be handled in caller."));
+
+ m2::CalculateBoundingBox doCalcBox;
+ for (auto const & p : geometry)
+ doCalcBox(p);
+
+ m2::CalculatePointOnSurface doCalcCenter(doCalcBox.GetBoundingBox());
+ for (auto i = 0; i < geometry.size() - 3; i += 3)
+ doCalcCenter(geometry[i], geometry[i + 1], geometry[i + 2]);
+
+ return doCalcCenter.GetCenter();
+}
+
+uint32_t GetGeometriesIntersectionCapacity(vector<m2::PointD> g1,
+ vector<m2::PointD> g2)
+{
+ struct Counter
+ {
+ Counter & operator++() { return *this; }
+ Counter & operator*() { return *this; }
+ Counter & operator=(m2::PointD const &)
+ {
+ ++m_count;
+ return *this;
+ }
+ uint32_t m_count = 0;
+ };
+
+ sort(begin(g1), end(g1));
+ sort(begin(g2), end(g2));
+
+ Counter counter;
+ set_intersection(begin(g1), end(g1),
+ begin(g2), end(g2),
+ counter, [](m2::PointD const & p1, m2::PointD const & p2)
+ {
+ // TODO(mgsergio): Use 1e-7 everyware instead of MercatotBounds::GetCellID2PointAbsEpsilon
+ return p1 < p2 && !p1.EqualDxDy(p2, 1e-7);
+ });
+ return counter.m_count;
+}
+} // namespace
+
+namespace editor
+{
+FeatureID MigrateNodeFeatureIndex(osm::Editor::TForEachFeaturesNearByFn & forEach,
+ XMLFeature const & xml)
+{
+ unique_ptr<FeatureType> feature;
+ auto count = 0;
+ forEach(
+ [&feature, &xml, &count](FeatureType const & ft)
+ {
+ if (ft.GetFeatureType() != feature::GEOM_POINT)
+ return;
+ // TODO(mgsergio): Check that ft and xml correspond to the same feature.
+ feature = make_unique<FeatureType>(ft);
+ ++count;
+ },
+ MercatorBounds::FromLatLon(xml.GetCenter()));
+ if (!feature)
+ MYTHROW(MigrationError, ("No pointed features returned."));
+ if (count > 1)
+ {
+ LOG(LWARNING,
+ (count, "features returned for point", MercatorBounds::FromLatLon(xml.GetCenter())));
+ }
+ return feature->GetID();
+}
+
+FeatureID MigrateWayFeatureIndex(osm::Editor::TForEachFeaturesNearByFn & forEach,
+ XMLFeature const & xml)
+{
+ unique_ptr<FeatureType> feature;
+ auto bestScore = 0.6; // initial score is used as a threshold.
+ auto const geometry = xml.GetGeometry();
+
+ if (geometry.empty() || geometry.size() % 3 != 0)
+ MYTHROW(MigrationError, ("Feature has invalid geometry", xml));
+
+ auto const someFeaturePoint = CalculateCenter(geometry);
+ auto count = 0;
+ LOG(LDEBUG, ("SomePoint", someFeaturePoint));
+ forEach(
+ [&feature, &xml, &geometry, &count, &bestScore](FeatureType const & ft)
+ {
+ if (ft.GetFeatureType() != feature::GEOM_AREA)
+ return;
+ ++count;
+ auto const ftGeometry = ft.GetTriangesAsPoints(FeatureType::BEST_GEOMETRY);
+ auto matched = GetGeometriesIntersectionCapacity(ftGeometry, geometry);
+ auto const score = static_cast<double>(matched) / geometry.size();
+ if (score > bestScore)
+ {
+ bestScore = score;
+ feature = make_unique<FeatureType>(ft);
+ }
+ },
+ someFeaturePoint);
+ if (count == 0)
+ MYTHROW(MigrationError, ("No ways returned for point", someFeaturePoint));
+ if (!feature)
+ {
+ MYTHROW(MigrationError,
+ ("None of returned ways suffice. Possibly, the feature have been deleted."));
+ }
+ return feature->GetID();
+}
+
+FeatureID MigrateFeatureIndex(osm::Editor::TForEachFeaturesNearByFn & forEach,
+ XMLFeature const & xml)
+{
+ switch (xml.GetType())
+ {
+ case XMLFeature::Type::Node: return MigrateNodeFeatureIndex(forEach, xml);
+ case XMLFeature::Type::Way: return MigrateWayFeatureIndex(forEach, xml);
+ }
+}
+} // namespace editor
diff --git a/indexer/edits_migration.hpp b/indexer/edits_migration.hpp
new file mode 100644
index 0000000000..53e6ba9f8e
--- /dev/null
+++ b/indexer/edits_migration.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "indexer/feature_decl.hpp"
+#include "indexer/osm_editor.hpp"
+
+#include "editor/xml_feature.hpp"
+
+#include "base/exception.hpp"
+
+namespace editor
+{
+DECLARE_EXCEPTION(MigrationError, RootException);
+
+/// Tries to match xml feature with one on a new mwm and retruns FeatrueID
+/// of a found feature, thows MigrationError if migration fails.
+FeatureID MigrateFeatureIndex(osm::Editor::TForEachFeaturesNearByFn & forEach,
+ XMLFeature const & xml);
+} // namespace editor
diff --git a/indexer/feature.cpp b/indexer/feature.cpp
index 5789fe9481..a5c43525ee 100644
--- a/indexer/feature.cpp
+++ b/indexer/feature.cpp
@@ -103,7 +103,7 @@ editor::XMLFeature FeatureType::ToXML() const
else
{
ParseTriangles(BEST_GEOMETRY);
- feature.SetGeometry({begin(m_triangles), end(m_triangles)});
+ feature.SetGeometry(begin(m_triangles), end(m_triangles));
}
ForEachName([&feature](uint8_t const & lang, string const & name)
diff --git a/indexer/feature.hpp b/indexer/feature.hpp
index 8559b15e98..ebd8ab8d5e 100644
--- a/indexer/feature.hpp
+++ b/indexer/feature.hpp
@@ -216,6 +216,7 @@ public:
ASSERT(m_bPointsParsed, ());
return m_points.size();
}
+
inline m2::PointD const & GetPoint(size_t i) const
{
ASSERT_LESS(i, m_points.size(), ());
@@ -235,6 +236,12 @@ public:
}
}
+ inline vector<m2::PointD> GetTriangesAsPoints(int scale) const
+ {
+ ParseTriangles(scale);
+ return {begin(m_triangles), end(m_triangles)};
+ }
+
template <typename TFunctor>
void ForEachTriangleEx(TFunctor && f, int scale) const
{
diff --git a/indexer/feature_algo.cpp b/indexer/feature_algo.cpp
index 30c822b2d0..3112d37b0b 100644
--- a/indexer/feature_algo.cpp
+++ b/indexer/feature_algo.cpp
@@ -1,6 +1,7 @@
#include "indexer/feature_algo.hpp"
#include "indexer/feature.hpp"
+#include "geometry/algorithm.hpp"
#include "geometry/distance.hpp"
#include "geometry/triangle2d.hpp"
@@ -9,99 +10,9 @@
namespace feature
{
-
-class CalculateLineCenter
-{
- typedef m2::PointD P;
-
- struct Value
- {
- Value(P const & p, double l) : m_p(p), m_len(l) {}
-
- bool operator< (Value const & r) const { return (m_len < r.m_len); }
-
- P m_p;
- double m_len;
- };
-
- vector<Value> m_poly;
- double m_length;
-
-public:
- CalculateLineCenter() : m_length(0.0) {}
-
- void operator() (m2::PointD const & pt)
- {
- m_length += (m_poly.empty() ? 0.0 : m_poly.back().m_p.Length(pt));
- m_poly.emplace_back(pt, m_length);
- }
-
- P GetCenter() const
- {
- typedef vector<Value>::const_iterator IterT;
-
- double const l = m_length / 2.0;
-
- IterT e = lower_bound(m_poly.begin(), m_poly.end(), Value(m2::PointD(0, 0), l));
- if (e == m_poly.begin())
- {
- /// @todo It's very strange, but we have linear objects with zero length.
- LOG(LWARNING, ("Zero length linear object"));
- return e->m_p;
- }
-
- IterT b = e-1;
-
- double const f = (l - b->m_len) / (e->m_len - b->m_len);
-
- // For safety reasons (floating point calculations) do comparison instead of ASSERT.
- if (0.0 <= f && f <= 1.0)
- return (b->m_p * (1-f) + e->m_p * f);
- else
- return ((b->m_p + e->m_p) / 2.0);
- }
-};
-
-class CalculatePointOnSurface
-{
- typedef m2::PointD P;
- P m_center;
- P m_rectCenter;
- double m_squareDistanceToApproximate;
-
-public:
- CalculatePointOnSurface(m2::RectD const & rect)
- {
- m_rectCenter = rect.Center();
- m_center = m_rectCenter;
- m_squareDistanceToApproximate = numeric_limits<double>::max();
- }
-
- void operator() (P const & p1, P const & p2, P const & p3)
- {
- if (m_squareDistanceToApproximate == 0.0)
- return;
- if (m2::IsPointInsideTriangle(m_rectCenter, p1, p2, p3))
- {
- m_center = m_rectCenter;
- m_squareDistanceToApproximate = 0.0;
- return;
- }
- P triangleCenter(p1);
- triangleCenter += p2;
- triangleCenter += p3;
- triangleCenter = triangleCenter / 3.0;
-
- double triangleDistance = m_rectCenter.SquareLength(triangleCenter);
- if (triangleDistance <= m_squareDistanceToApproximate)
- {
- m_center = triangleCenter;
- m_squareDistanceToApproximate = triangleDistance;
- }
- }
- P GetCenter() const { return m_center; }
-};
-
+/// @returns point on a feature that is the closest to f.GetLimitRect().Center().
+/// It is used by many ednities in the core of mapsme. Do not modify it's
+/// logic if you really-really know what you are doing.
m2::PointD GetCenter(FeatureType const & f, int scale)
{
EGeomType const type = f.GetFeatureType();
@@ -112,7 +23,7 @@ m2::PointD GetCenter(FeatureType const & f, int scale)
case GEOM_LINE:
{
- CalculateLineCenter doCalc;
+ m2::CalculatePolyLineCenter doCalc;
f.ForEachPoint(doCalc, scale);
return doCalc.GetCenter();
}
@@ -120,7 +31,7 @@ m2::PointD GetCenter(FeatureType const & f, int scale)
default:
{
ASSERT_EQUAL(type, GEOM_AREA, ());
- CalculatePointOnSurface doCalc(f.GetLimitRect(scale));
+ m2::CalculatePointOnSurface doCalc(f.GetLimitRect(scale));
f.ForEachTriangle(doCalc, scale);
return doCalc.GetCenter();
}
@@ -197,5 +108,4 @@ double GetMinDistanceMeters(FeatureType const & ft, m2::PointD const & pt)
{
return GetMinDistanceMeters(ft, pt, FeatureType::BEST_GEOMETRY);
}
-
} // namespace feature
diff --git a/indexer/indexer.pro b/indexer/indexer.pro
index ae7e529cd7..8036093582 100644
--- a/indexer/indexer.pro
+++ b/indexer/indexer.pro
@@ -19,6 +19,7 @@ SOURCES += \
drawing_rules.cpp \
drules_selector.cpp \
drules_selector_parser.cpp \
+ edits_migration.cpp \
feature.cpp \
feature_algo.cpp \
feature_covering.cpp \
@@ -61,6 +62,7 @@ HEADERS += \
drules_include.hpp \
drules_selector.hpp \
drules_selector_parser.hpp \
+ edits_migration.hpp \
feature.hpp \
feature_algo.hpp \
feature_covering.hpp \
diff --git a/indexer/osm_editor.cpp b/indexer/osm_editor.cpp
index fabbb091dc..d670491211 100644
--- a/indexer/osm_editor.cpp
+++ b/indexer/osm_editor.cpp
@@ -1,4 +1,5 @@
#include "indexer/classificator.hpp"
+#include "indexer/edits_migration.hpp"
#include "indexer/feature_algo.hpp"
#include "indexer/feature_decl.hpp"
#include "indexer/feature_impl.hpp"
@@ -17,6 +18,8 @@
#include "coding/internal/file_data.hpp"
+#include "geometry/algorithm.hpp"
+
#include "base/exception.hpp"
#include "base/logging.hpp"
#include "base/stl_helpers.hpp"
@@ -230,15 +233,6 @@ TypeDescription const * GetTypeDescription(uint32_t type, uint8_t typeTruncateLe
return nullptr;
}
-uint32_t MigrateFeatureIndex(XMLFeature const & /*xml*/)
-{
- // @TODO(mgsergio): Update feature's index when user has downloaded fresh MWM file and old indices point to other features.
- // Possible implementation: use function to load features in rect (center feature's point) and somehow compare/choose from them.
- // Probably we need to store more data about features in xml, e.g. types, may be other data, to match them correctly.
- MYTHROW(RootException, ("TODO(mgsergio, AlexZ): Implement correct feature migrate code. Delete data/edits.xml to continue."));
- return 0;
-}
-
/// Compares editable fields connected with feature ignoring street.
bool AreFeaturesEqualButStreet(FeatureType const & a, FeatureType const & b)
{
@@ -268,18 +262,11 @@ XMLFeature GetMatchingFeatureFromOSM(osm::ChangesetWrapper & cw,
if (featurePtr->GetFeatureType() == feature::GEOM_POINT)
return cw.GetMatchingNodeFeatureFromOSM(featurePtr->GetCenter());
- vector<m2::PointD> geometry;
- featurePtr->ForEachTriangle(
- [&geometry](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
- {
- geometry.push_back(p1);
- geometry.push_back(p2);
- geometry.push_back(p3);
- // Warning: geometry is cached in FeatureType. So if it wasn't BEST_GEOMETRY,
- // we can never have it. Features here came from editors loader and should
- // have BEST_GEOMETRY geometry.
- },
- FeatureType::BEST_GEOMETRY);
+ // Warning: geometry is cached in FeatureType. So if it wasn't BEST_GEOMETRY,
+ // we can never have it. Features here came from editors loader and should
+ // have BEST_GEOMETRY geometry.
+ auto geometry = featurePtr->GetTriangesAsPoints(FeatureType::BEST_GEOMETRY);
+
// Filters out duplicate points for closed ways or triangles' vertices.
my::SortUnique(geometry);
@@ -328,17 +315,18 @@ void Editor::LoadMapEdits()
}};
int deleted = 0, modified = 0, created = 0;
+ bool needRewriteEdits = false;
+
for (xml_node mwm : doc.child(kXmlRootNode).children(kXmlMwmNode))
{
string const mapName = mwm.attribute("name").as_string("");
int64_t const mapVersion = mwm.attribute("version").as_llong(0);
- MwmSet::MwmId const id = m_mwmIdByMapNameFn(mapName);
- if (!id.IsAlive())
- {
- // TODO(AlexZ): MWM file was deleted, but changes remain. What should we do in this case?
- LOG(LWARNING, (mapName, "version", mapVersion, "references not existing MWM file."));
- continue;
- }
+ MwmSet::MwmId const mwmId = m_mwmIdByMapNameFn(mapName);
+ // TODO(mgsergio, AlexZ): Get rid of mapVersion and switch to upload_time.
+ // TODO(mgsergio, AlexZ): Is it normal to have isMwmIdAlive and mapVersion
+ // NOT equal to mwmId.GetInfo()->GetVersion() at the same time?
+ auto const needMigrateEdits = !mwmId.IsAlive() || mapVersion != mwmId.GetInfo()->GetVersion();
+ needRewriteEdits |= needMigrateEdits;
for (auto const & section : sections)
{
@@ -347,12 +335,11 @@ void Editor::LoadMapEdits()
try
{
XMLFeature const xml(nodeOrWay.node());
- // TODO(mgsergio, AlexZ): MigrateFeatureIndex() case will always throw now, so 'old' features are ignored.
- uint32_t const featureIndex = mapVersion == id.GetInfo()->GetVersion() ? xml.GetMWMFeatureIndex()
- : MigrateFeatureIndex(xml);
- FeatureID const fid(id, featureIndex);
+ auto const fid = needMigrateEdits
+ ? MigrateFeatureIndex(m_forEachFeatureAtPointFn, xml)
+ : FeatureID(mwmId, xml.GetMWMFeatureIndex());
- FeatureTypeInfo & fti = m_features[id][fid.m_index];
+ FeatureTypeInfo & fti = m_features[fid.m_mwmId][fid.m_index];
if (section.first == FeatureStatus::Created)
{
@@ -387,10 +374,17 @@ void Editor::LoadMapEdits()
nodeOrWay.node().print(s, " ");
LOG(LERROR, (ex.what(), "Can't create XMLFeature in section", section.second, s.str()));
}
+ catch (editor::MigrationError const & ex)
+ {
+ LOG(LWARNING, (ex.Msg(), "mwmId =", mwmId, XMLFeature(nodeOrWay.node())));
+ }
} // for nodes
} // for sections
} // for mwms
+ // Save edits with new indexes and mwm version to avoid another migration on next startup.
+ if (needRewriteEdits)
+ Save(GetEditorFilePath());
LOG(LINFO, ("Loaded", modified, "modified,", created, "created and", deleted, "deleted features."));
}
@@ -623,10 +617,6 @@ vector<uint32_t> Editor::GetFeaturesByStatus(MwmSet::MwmId const & mwmId, Featur
vector<Metadata::EType> Editor::EditableMetadataForType(FeatureType const & feature) const
{
- // Cannot edit old mwm data.
- if (platform::migrate::NeedMigrate())
- return {};
-
// TODO(mgsergio): Load editable fields into memory from XML and query them here.
feature::TypesHolder const types(feature);
set<Metadata::EType> fields;
@@ -658,10 +648,6 @@ vector<Metadata::EType> Editor::EditableMetadataForType(FeatureType const & feat
bool Editor::IsNameEditable(FeatureType const & feature) const
{
- // Cannot edit old mwm data.
- if (platform::migrate::NeedMigrate())
- return false;
-
feature::TypesHolder const types(feature);
for (auto type : types)
{
@@ -674,10 +660,6 @@ bool Editor::IsNameEditable(FeatureType const & feature) const
bool Editor::IsAddressEditable(FeatureType const & feature) const
{
- // Cannot edit old mwm data.
- if (platform::migrate::NeedMigrate())
- return false;
-
feature::TypesHolder const types(feature);
auto & isBuilding = ftypes::IsBuildingChecker::Instance();
for (auto type : types)
diff --git a/indexer/osm_editor.hpp b/indexer/osm_editor.hpp
index b1efc2e4c5..4339517cf4 100644
--- a/indexer/osm_editor.hpp
+++ b/indexer/osm_editor.hpp
@@ -2,6 +2,7 @@
#include "geometry/rect2d.hpp"
+#include "indexer/feature.hpp"
#include "indexer/feature_meta.hpp"
#include "indexer/mwm_set.hpp"
@@ -15,10 +16,6 @@
#include "std/set.hpp"
#include "std/string.hpp"
-struct FeatureID;
-class FeatureType;
-class Index;
-
namespace osm
{
class Editor final
@@ -26,10 +23,14 @@ class Editor final
Editor() = default;
public:
+ using TFeatureTypeFn = function<void(FeatureType &)>; // Mimics Framework::TFeatureTypeFn.
+
using TMwmIdByMapNameFn = function<MwmSet::MwmId(string const & /*map*/)>;
using TInvalidateFn = function<void()>;
using TFeatureLoaderFn = function<unique_ptr<FeatureType> (FeatureID const & /*fid*/)>;
using TFeatureOriginalStreetFn = function<string(FeatureType const & /*ft*/)>;
+ using TForEachFeaturesNearByFn =
+ function<void(TFeatureTypeFn && /*fn*/, m2::PointD const & /*mercator*/)>;
enum class UploadResult
{
Success,
@@ -52,6 +53,7 @@ public:
void SetInvalidateFn(TInvalidateFn const & fn) { m_invalidateFn = fn; }
void SetFeatureLoaderFn(TFeatureLoaderFn const & fn) { m_getOriginalFeatureFn = fn; }
void SetFeatureOriginalStreetFn(TFeatureOriginalStreetFn const & fn) { m_getOriginalFeatureStreetFn = fn; }
+ void SetForEachFeatureAtPointFn(TForEachFeaturesNearByFn const & fn) { m_forEachFeatureAtPointFn = fn; }
void LoadMapEdits();
/// Resets editor to initial state: no any edits or created/deleted features.
@@ -146,6 +148,8 @@ private:
TFeatureLoaderFn m_getOriginalFeatureFn;
/// Get feature original street name or empty string.
TFeatureOriginalStreetFn m_getOriginalFeatureStreetFn;
+ /// Iterate over all features in some area that includes given point.
+ TForEachFeaturesNearByFn m_forEachFeatureAtPointFn;
}; // class Editor
string DebugPrint(Editor::FeatureStatus fs);
diff --git a/map/framework.cpp b/map/framework.cpp
index c04052c283..8320bf4818 100644
--- a/map/framework.cpp
+++ b/map/framework.cpp
@@ -360,6 +360,7 @@ Framework::Framework()
return streets.first[streets.second].m_name;
return {};
});
+ editor.SetForEachFeatureAtPointFn(bind(&Framework::ForEachFeatureAtPoint, this, _1, _2));
editor.LoadMapEdits();
}