diff --git a/generator/feature_merger.cpp b/generator/feature_merger.cpp index a7ea704234..beefb7cf2c 100644 --- a/generator/feature_merger.cpp +++ b/generator/feature_merger.cpp @@ -4,40 +4,53 @@ #include "../indexer/classificator.hpp" -MergedFeatureBuilder1::MergedFeatureBuilder1(FeatureBuilder1 const & fb, bool isOK) - : FeatureBuilder1(fb), m_isOK(isOK) +MergedFeatureBuilder1::MergedFeatureBuilder1(FeatureBuilder1 const & fb) + : FeatureBuilder1(fb), m_isRound(false) { m_Params.FinishAddingTypes(); } -void MergedFeatureBuilder1::AppendFeature(MergedFeatureBuilder1 const & fb, bool toBack) +void MergedFeatureBuilder1::SetRound() { - if (fb.m_isOK) - m_isOK = true; + m_isRound = true; + m_roundBounds[0] = m_roundBounds[1] = m_Geometry; +} - m2::PointD const pt = toBack ? LastPoint() : FirstPoint(); +void MergedFeatureBuilder1::AppendFeature(MergedFeatureBuilder1 const & fb, bool fromBegin, bool toBack) +{ + if (fb.m_isRound) + { + if (toBack) + m_roundBounds[1] = fb.m_Geometry; + else + m_roundBounds[0] = fb.m_Geometry; + return; + } - bool fromEnd = false; - if ((pt.SquareLength(fb.FirstPoint()) > pt.SquareLength(fb.LastPoint())) == toBack) - fromEnd = true; + if (toBack) + m_roundBounds[1].clear(); + else + m_roundBounds[0].clear(); + + m_isRound = false; for (size_t i = 0; i < fb.m_Geometry.size(); ++i) m_LimitRect.Add(fb.m_Geometry[i]); - if (fromEnd) - { - if (toBack) - m_Geometry.insert(m_Geometry.end(), fb.m_Geometry.rbegin() + 1, fb.m_Geometry.rend()); - else - m_Geometry.insert(m_Geometry.begin(), fb.m_Geometry.rbegin(), fb.m_Geometry.rend() - 1); - } - else + if (fromBegin) { if (toBack) m_Geometry.insert(m_Geometry.end(), fb.m_Geometry.begin() + 1, fb.m_Geometry.end()); else m_Geometry.insert(m_Geometry.begin(), fb.m_Geometry.begin(), fb.m_Geometry.end() - 1); } + else + { + if (toBack) + m_Geometry.insert(m_Geometry.end(), fb.m_Geometry.rbegin() + 1, fb.m_Geometry.rend()); + else + m_Geometry.insert(m_Geometry.begin(), fb.m_Geometry.rbegin(), fb.m_Geometry.rend() - 1); + } } bool MergedFeatureBuilder1::EqualGeometry(MergedFeatureBuilder1 const & fb) const @@ -45,6 +58,36 @@ bool MergedFeatureBuilder1::EqualGeometry(MergedFeatureBuilder1 const & fb) cons return (m_Geometry == fb.m_Geometry); } +pair MergedFeatureBuilder1::GetKeyPoint(size_t i) const +{ + size_t sz = m_roundBounds[0].size(); + if (i < sz) return make_pair(m_roundBounds[0][i], false); + i -= sz; + + if (i == 0) return make_pair(FirstPoint(), false); + + sz = m_roundBounds[1].size(); + if (i < sz) return make_pair(m_roundBounds[1][i], true); + + i -= sz; + + ASSERT_EQUAL ( i, 1, () ); + return make_pair(LastPoint(), true); +} + +size_t MergedFeatureBuilder1::GetKeyPointsCount() const +{ + return m_roundBounds[0].size() + m_roundBounds[1].size() + 2; +} + +double MergedFeatureBuilder1::GetPriority() const +{ + double pr = 0.0; + for (size_t i = 1; i < m_Geometry.size(); ++i) + pr += m_Geometry[i-1].SquareLength(m_Geometry[i]); + return pr; +} + FeatureMergeProcessor::key_t FeatureMergeProcessor::get_key(m2::PointD const & p) { @@ -58,7 +101,7 @@ FeatureMergeProcessor::FeatureMergeProcessor(uint32_t coordBits) void FeatureMergeProcessor::operator() (FeatureBuilder1 const & fb) { - this->operator() (new MergedFeatureBuilder1(fb, true)); + this->operator() (new MergedFeatureBuilder1(fb)); } void FeatureMergeProcessor::operator() (MergedFeatureBuilder1 * p) @@ -69,16 +112,29 @@ void FeatureMergeProcessor::operator() (MergedFeatureBuilder1 * p) m_map[k1].push_back(p); if (k1 != k2) m_map[k2].push_back(p); + else + { + ///@ todo Do it only for small round features! + p->SetRound(); + + p->ForEachMiddlePoints(bind(&FeatureMergeProcessor::Insert, this, _1, p)); + } +} + +void FeatureMergeProcessor::Insert(m2::PointD const & pt, MergedFeatureBuilder1 * p) +{ + m_map[get_key(pt)].push_back(p); } void FeatureMergeProcessor::Remove(key_t key, MergedFeatureBuilder1 const * p) { map_t::iterator i = m_map.find(key); - CHECK(i != m_map.end(), ()); - - vector_t & v = i->second; - v.erase(remove(v.begin(), v.end(), p), v.end()); - if (v.empty()) m_map.erase(i); + if (i != m_map.end()) + { + vector_t & v = i->second; + v.erase(remove(v.begin(), v.end(), p), v.end()); + if (v.empty()) m_map.erase(i); + } } void FeatureMergeProcessor::Remove(MergedFeatureBuilder1 const * p) @@ -89,6 +145,12 @@ void FeatureMergeProcessor::Remove(MergedFeatureBuilder1 const * p) Remove(k1, p); if (k1 != k2) Remove(k2, p); + else + { + ASSERT ( p->IsRound(), () ); + + p->ForEachMiddlePoints(bind(&FeatureMergeProcessor::Remove1, this, _1, p)); + } } void FeatureMergeProcessor::DoMerge(FeatureEmitterIFace & emitter) @@ -113,53 +175,65 @@ void FeatureMergeProcessor::DoMerge(FeatureEmitterIFace & emitter) MergedFeatureBuilder1 curr(*p); curr.SetType(type); - // Cycle while merging. - while (true) + // Iterate through key points while merging. + size_t ind = 0; + while (ind < curr.GetKeyPointsCount()) // GetKeyPointsCount() can be different on each iteration { - bool isMerged = false; - - bool toBack = true; - map_t::iterator it = m_map.find(get_key(curr.LastPoint())); - if (it == m_map.end()) - { - it = m_map.find(get_key(curr.FirstPoint())); - toBack = false; - } + pair const pt = curr.GetKeyPoint(ind++); + map_t::const_iterator it = m_map.find(get_key(pt.first)); + MergedFeatureBuilder1 * pp = 0; if (it != m_map.end()) { // Find best feature to continue. + double bestPr = -1.0; for (size_t i = 0; i < it->second.size(); ++i) { - MergedFeatureBuilder1 * pp = it->second[i]; - - if (pp->HasType(type)) + MergedFeatureBuilder1 * pTest = it->second[i]; + if (pTest->HasType(type)) { - isMerged = true; - curr.AppendFeature(*pp, toBack); - - if (pp->PopExactType(type)) + double const pr = pTest->GetPriority(); + ASSERT_GREATER ( pr, 0.0, () ); + if (pr > bestPr) { - Remove(pp); - delete pp; + pp = pTest; + bestPr = pr; } - break; } } - } - if (!isMerged) - break; + // Merge current feature with best feature. + if (pp) + { + bool const toBack = pt.second; + bool fromBegin = true; + if ((pt.first.SquareLength(pp->FirstPoint()) > pt.first.SquareLength(pp->LastPoint())) == toBack) + fromBegin = false; + + curr.AppendFeature(*pp, fromBegin, toBack); + + if (pp->PopExactType(type)) + { + Remove(pp); + delete pp; + } + + // start from the beginning if we have a successful merge + ind = 0; + } + } } - // Push feature p to output. if (m_last.NotEmpty() && m_last.EqualGeometry(curr)) + { + // curr is equal with m_last by geometry - just add new type to m_last m_last.AddType(type); + } else { + // emit m_last and set curr as last processed feature (m_last) if (m_last.NotEmpty()) emitter(m_last); - m_last = curr; } @@ -204,7 +278,7 @@ void FeatureTypesProcessor::SetMappingTypes(char const * arr1[2], char const * a MergedFeatureBuilder1 * FeatureTypesProcessor::operator() (FeatureBuilder1 const & fb) { - MergedFeatureBuilder1 * p = new MergedFeatureBuilder1(fb, true); + MergedFeatureBuilder1 * p = new MergedFeatureBuilder1(fb); p->ForEachChangeTypes(do_change_types(*this)); diff --git a/generator/feature_merger.hpp b/generator/feature_merger.hpp index 1ef1224899..bc2c6bdbfb 100644 --- a/generator/feature_merger.hpp +++ b/generator/feature_merger.hpp @@ -10,13 +10,18 @@ /// Feature builder class that used while feature type processing and merging. class MergedFeatureBuilder1 : public FeatureBuilder1 { - bool m_isOK; + bool m_isRound; + + points_t m_roundBounds[2]; public: - MergedFeatureBuilder1() : m_isOK(false) {} - MergedFeatureBuilder1(FeatureBuilder1 const & fb, bool isOK); + MergedFeatureBuilder1() : m_isRound(false) {} + MergedFeatureBuilder1(FeatureBuilder1 const & fb); - void AppendFeature(MergedFeatureBuilder1 const & fb, bool toBack); + void SetRound(); + bool IsRound() const { return m_isRound; } + + void AppendFeature(MergedFeatureBuilder1 const & fb, bool fromBegin, bool toBack); bool EqualGeometry(MergedFeatureBuilder1 const & fb) const; @@ -34,6 +39,17 @@ public: for_each(m_Params.m_Types.begin(), m_Params.m_Types.end(), toDo); m_Params.FinishAddingTypes(); } + + template void ForEachMiddlePoints(ToDo toDo) const + { + for (size_t i = 1; i < m_Geometry.size()-1; ++i) + toDo(m_Geometry[i]); + } + + pair GetKeyPoint(size_t i) const; + size_t GetKeyPointsCount() const; + + double GetPriority() const; }; /// Feature merger. @@ -48,7 +64,13 @@ class FeatureMergeProcessor typedef map map_t; map_t m_map; + void Insert(m2::PointD const & pt, MergedFeatureBuilder1 * p); + void Remove(key_t key, MergedFeatureBuilder1 const * p); + inline void Remove1(m2::PointD const & pt, MergedFeatureBuilder1 const * p) + { + Remove(get_key(pt), p); + } void Remove(MergedFeatureBuilder1 const * p); uint32_t m_coordBits; diff --git a/generator/generator_tests/feature_merger_test.cpp b/generator/generator_tests/feature_merger_test.cpp index 1364b8a241..5fa5d5417d 100644 --- a/generator/generator_tests/feature_merger_test.cpp +++ b/generator/generator_tests/feature_merger_test.cpp @@ -130,5 +130,5 @@ UNIT_TEST(FeatureMerger_Branches) VectorEmitter emitter; processor.DoMerge(emitter); - TEST_EQUAL(emitter.GetSize(), 1, ()); + TEST_LESS_OR_EQUAL(emitter.GetSize(), 2, ()); } diff --git a/generator/world_map_generator.hpp b/generator/world_map_generator.hpp index 07a45a05a0..275e3257e7 100644 --- a/generator/world_map_generator.hpp +++ b/generator/world_map_generator.hpp @@ -12,8 +12,9 @@ inline int GetMinFeatureDrawScale(FeatureBuilder1 const & fb) { FeatureBase fBase = fb.GetFeatureBase(); int const minScale = feature::MinDrawableScaleForFeature(fBase); - CHECK_GREATER(minScale, -1, ("Non-drawable feature found!?")); - return minScale; + + // some features become invisible after merge processing, so -1 is possible + return (minScale == -1 ? 1000 : minScale); }