From 81e732a71d79d31cd41b0c54020ae23b8f2961ea Mon Sep 17 00:00:00 2001 From: Yury Melnichek Date: Mon, 25 Apr 2011 03:49:24 +0200 Subject: [PATCH] Rewrite feature covering. Use a simpler approach and remove stream optimizer. --- geometry/covering.hpp | 379 +++--------------- geometry/covering_stream_optimizer.hpp | 121 ------ geometry/geometry.pro | 1 - .../covering_stream_optimizer_test.cpp | 63 --- geometry/geometry_tests/covering_test.cpp | 125 +----- geometry/geometry_tests/geometry_tests.pro | 1 - indexer/cell_coverer.hpp | 121 +----- indexer/covering.cpp | 141 +++---- indexer/covering.hpp | 3 +- indexer/indexer_tests/cell_coverer_test.cpp | 118 ------ indexer/indexer_tests/index_builder_test.cpp | 3 +- indexer/scale_index_builder.hpp | 38 +- 12 files changed, 140 insertions(+), 974 deletions(-) delete mode 100644 geometry/covering_stream_optimizer.hpp delete mode 100644 geometry/geometry_tests/covering_stream_optimizer_test.cpp diff --git a/geometry/covering.hpp b/geometry/covering.hpp index c774191275..f7fd6869af 100644 --- a/geometry/covering.hpp +++ b/geometry/covering.hpp @@ -3,31 +3,26 @@ #include "../base/assert.hpp" #include "../base/base.hpp" #include "../base/logging.hpp" -#include "../base/set_operations.hpp" -#include "../base/stl_add.hpp" +#include "../base/math.hpp" #include "../std/algorithm.hpp" -#include "../std/array.hpp" -#include "../std/functional.hpp" -#include "../std/iterator.hpp" -#include "../std/map.hpp" -#include "../std/queue.hpp" #include "../std/utility.hpp" #include "../std/vector.hpp" namespace covering { -enum CellTriangleIntersection +enum CellObjectIntersection { - CELL_TRIANGLE_NO_INTERSECTION = 0, - CELL_TRIANGLE_INTERSECT = 1, - CELL_INSIDE_TRIANGLE = 2, - TRIANGLE_INSIDE_CELL = 3 + CELL_OBJECT_NO_INTERSECTION = 0, + CELL_OBJECT_INTERSECT = 1, + CELL_INSIDE_OBJECT = 2, + OBJECT_INSIDE_CELL = 3 }; template -CellTriangleIntersection IntersectCellWithTriangle( - CellIdT const cell, m2::PointD const & a, m2::PointD const & b, m2::PointD const & c) +inline CellObjectIntersection IntersectCellWithLine(CellIdT const cell, + m2::PointD const & a, + m2::PointD const & b) { pair const xy = cell.XY(); uint32_t const r = cell.Radius(); @@ -39,308 +34,66 @@ CellTriangleIntersection IntersectCellWithTriangle( m2::PointD(xy.first + r, xy.second - r) }; for (int i = 0; i < 4; ++i) - { - m2::PointD const & p1 = cellCorners[i]; - m2::PointD const & p2 = cellCorners[i == 0 ? 3 : i - 1]; - if (m2::SegmentsIntersect(a, b, p1, p2)) return CELL_TRIANGLE_INTERSECT; - if (m2::SegmentsIntersect(b, c, p1, p2)) return CELL_TRIANGLE_INTERSECT; - if (m2::SegmentsIntersect(c, a, p1, p2)) return CELL_TRIANGLE_INTERSECT; - } + if (m2::SegmentsIntersect(a, b, cellCorners[i], cellCorners[i == 0 ? 3 : i - 1])) + return CELL_OBJECT_INTERSECT; if (xy.first - r <= a.x && a.x <= xy.first + r && xy.second - r <= a.y && a.y <= xy.second + r) - return TRIANGLE_INSIDE_CELL; - if (m2::IsPointStrictlyInsideTriangle(cellCorners[0], a, b, c)) - return CELL_INSIDE_TRIANGLE; - return CELL_TRIANGLE_NO_INTERSECTION; -} - -template -void CoverTriangle(m2::PointD const & a, m2::PointD const & b, m2::PointD const & c, - F const & output, uint8_t const level = CellIdT::DEPTH_LEVELS - 1, - CellIdT const startCell = CellIdT::Root()) -{ - ASSERT_LESS_OR_EQUAL(startCell.Level(), level, (a, b, c)); - CellTriangleIntersection intersection = IntersectCellWithTriangle(startCell, a, b, c); - - if (intersection == CELL_TRIANGLE_NO_INTERSECTION) - return; - - if (startCell.Level() == level || intersection == CELL_INSIDE_TRIANGLE) - { - output(startCell); - return; - } - - for (uint8_t child = 0; child < 4; ++child) - CoverTriangle(a, b, c, output, level, startCell.Child(child)); + return OBJECT_INSIDE_CELL; + return CELL_OBJECT_NO_INTERSECTION; } template -class Covering +CellObjectIntersection IntersectCellWithTriangle( + CellIdT const cell, m2::PointD const & a, m2::PointD const & b, m2::PointD const & c) { -public: - typedef CellIdT CellId; - typedef typename CellId::LessQueueOrder LessQueueOrder; - - Covering() : m_Size(0) {} - - explicit Covering(CellId cell) : m_Size(1) - { - m_Covering[cell.Level()].push_back(cell); - } - - explicit Covering(vector const & v, int64_t minId = 0) - { - for (size_t i = 0; i < v.size(); ++i) - m_Covering[v[i].Level()].push_back(v[i]); - Sort(); - Unique(); - RemoveDuplicateChildren(); - RemoveFullSquares(minId); - m_Size = CalculateSize(); - } - - // Cover triangle. - Covering(m2::PointD const & a, m2::PointD const & b, m2::PointD const & c, - int level = CellId::DEPTH_LEVELS - 1) - { - // TODO: ASSERT(a != b), ASSERT(b != c), ASSERT(a != c) ? - ASSERT(0 <= level && level <= CellId::DEPTH_LEVELS, (level, CellId::Root())); - CoverTriangleInfo info; - info.m_pCovering = this; - info.m_A = a; - info.m_B = b; - info.m_C = c; - info.m_Level = level; - CoverTriangleImpl(info, CellId::Root()); - Sort(); - RemoveFullSquares(); - m_Size = CalculateSize(); - } - - size_t Size() const - { - ASSERT_EQUAL(m_Size, CalculateSize(), ()); - return m_Size; - } - - void Append(Covering const & c) - { - AppendWithoutNormalize(c); - RemoveDuplicateChildren(); - RemoveFullSquares(); - m_Size = CalculateSize(); - } - - void OutputToVector(vector & result) const - { - for (int level = 0; level < CellId::DEPTH_LEVELS; ++level) - result.insert(result.end(), m_Covering[level].begin(), m_Covering[level].end()); - } - - void OutputToVector(vector & result) const - { - for (int level = 0; level < CellId::DEPTH_LEVELS; ++level) - for (size_t i = 0; i < m_Covering[level].size(); ++i) - result.push_back(m_Covering[level][i].ToInt64()); - } - - void Simplify(int64_t minId = 0) - { - int cellsSimplified = 0; - int const initialSize = m_Size; - for (int level = CellId::DEPTH_LEVELS - 1; level > 1; --level) - { - if (m_Covering[level].size() >= 2) - { - int const initialLevelSize = static_cast(m_Covering[level].size()); - SimplifyLevel(level, minId); - cellsSimplified += initialLevelSize - static_cast(m_Covering[level].size()); - if (cellsSimplified > initialSize / 2) - break; - } - } - RemoveDuplicateChildren(); - RemoveFullSquares(minId); - m_Size = CalculateSize(); - } - -private: - - void SimplifyLevel(int level, int64_t minId) - { - map parentCellCounts; - typedef typename vector::const_iterator ConstIteartor; - for (ConstIteartor it = m_Covering[level].begin(); it != m_Covering[level].end(); ++it) - if (it->Parent().ToInt64() >= minId) - ++parentCellCounts[it->Parent()]; - - vector parentCells, childCells; - for (ConstIteartor it = m_Covering[level].begin(); it != m_Covering[level].end(); ++it) - { - if (parentCellCounts[it->Parent()] > 1) - parentCells.push_back(it->Parent()); - else - childCells.push_back(*it); - } - ASSERT(IsSorted(parentCells.begin(), parentCells.end(), LessQueueOrder()), (parentCells)); - ASSERT(IsSorted(childCells.begin(), childCells.end(), LessQueueOrder()), (childCells)); - m_Covering[level].swap(childCells); - parentCells.erase(unique(parentCells.begin(), parentCells.end()), parentCells.end()); - AppendToVector(m_Covering[level - 1], parentCells); - } - - static void AppendToVector(vector & a, vector const & b) - { - ASSERT(IsSortedAndUnique(a.begin(), a.end(), LessQueueOrder()), (a)); - ASSERT(IsSortedAndUnique(b.begin(), b.end(), LessQueueOrder()), (b)); - vector merged; - set_union(a.begin(), a.end(), b.begin(), b.end(), back_inserter(merged), LessQueueOrder()); - a.swap(merged); - } - - template - struct CompareCellsAtLevel - { - explicit CompareCellsAtLevel(int level) : m_Level(level) {} - - bool operator() (CellId id1, CellId id2) const - { - return m_Comp(id1.AncestorAtLevel(m_Level), id2.AncestorAtLevel(m_Level)); - } - - CompareT m_Comp; - int m_Level; - }; - - void AppendWithoutNormalize(Covering const & c) - { - for (int level = CellId::DEPTH_LEVELS - 1; level >= 0; --level) - AppendToVector(m_Covering[level], c.m_Covering[level]); - } - - void Sort() - { - for (int level = 0; level < CellId::DEPTH_LEVELS; ++level) - sort(m_Covering[level].begin(), m_Covering[level].end(), LessQueueOrder()); - } - - void Unique() - { - for (int level = 0; level < CellId::DEPTH_LEVELS; ++level) - { - vector & covering = m_Covering[level]; - ASSERT(IsSorted(covering.begin(), covering.end(), LessQueueOrder()), (covering)); - covering.erase(unique(covering.begin(), covering.end()), covering.end()); - } - } - - void RemoveDuplicateChildren() - { - RemoveDuplicateChildrenImpl(); -#ifdef DEBUG - // Assert that all duplicate children were removed. - vector v1, v2; - OutputToVector(v1); - RemoveDuplicateChildrenImpl(); - OutputToVector(v2); - ASSERT_EQUAL(v1, v2, ()); -#endif - } - - void RemoveDuplicateChildrenImpl() - { - for (int parentLevel = 0; parentLevel < static_cast(m_Covering.size()) - 1; ++parentLevel) - { - if (m_Covering[parentLevel].empty()) - continue; - for (int childLevel = parentLevel + 1; childLevel < static_cast(m_Covering.size()); - ++childLevel) - { - vector substracted; - CompareCellsAtLevel comparator(parentLevel); - ASSERT(IsSorted(m_Covering[childLevel].begin(), m_Covering[childLevel].end(), comparator), - (m_Covering[childLevel])); - ASSERT(IsSorted(m_Covering[parentLevel].begin(), m_Covering[parentLevel].end(), comparator), - (m_Covering[parentLevel])); - SetDifferenceUnlimited(m_Covering[childLevel].begin(), m_Covering[childLevel].end(), - m_Covering[parentLevel].begin(), m_Covering[parentLevel].end(), - back_inserter(substracted), comparator); - m_Covering[childLevel].swap(substracted); - } - } - } - - void RemoveFullSquares(int64_t minId = 0) - { - vector cellsToAppend; - for (int level = m_Covering.size() - 1; level >= 0; --level) - { - // a -> b + parents - vector const & a = m_Covering[level]; - vector b; - vector parents; - b.reserve(a.size()); - for (size_t i = 0; i < a.size(); ++i) - { - if (i + 3 < a.size()) - { - CellId const parent = a[i].Parent(); - if (parent == a[i+1].Parent() && - parent == a[i+2].Parent() && - parent == a[i+3].Parent() && - parent.ToInt64() >= minId) - { - parents.push_back(parent); - i += 3; - continue; - } - } - b.push_back(a[i]); - } - m_Covering[level].swap(b); - if (level > 0) - AppendToVector(m_Covering[level - 1], parents); - } - } - - size_t CalculateSize() const - { - size_t size = 0; - for (int level = 0; level < CellId::DEPTH_LEVELS; ++level) - size += m_Covering[level].size(); - return size; - } - - struct CoverTriangleInfo - { - m2::PointD m_A, m_B, m_C; - int m_Level; - Covering * m_pCovering; - }; - - static void CoverTriangleImpl(CoverTriangleInfo const & info, CellId const cell) - { - ASSERT_LESS_OR_EQUAL(cell.Level(), info.m_Level, (info.m_A, info.m_B, info.m_C)); - CellTriangleIntersection intersection = - IntersectCellWithTriangle(cell, info.m_A, info.m_B, info.m_C); - - if (intersection == CELL_TRIANGLE_NO_INTERSECTION) - return; - - if (cell.Level() == info.m_Level || intersection == CELL_INSIDE_TRIANGLE) - { - info.m_pCovering->m_Covering[cell.Level()].push_back(cell); - return; - } - - for (uint8_t child = 0; child < 4; ++child) - CoverTriangleImpl(info, cell.Child(child)); - } - - - array, CellId::DEPTH_LEVELS> m_Covering; // Covering by level. - size_t m_Size; -}; - - + CellObjectIntersection const i1 = IntersectCellWithLine(cell, a, b); + if (i1 == CELL_OBJECT_INTERSECT) + return CELL_OBJECT_INTERSECT; + CellObjectIntersection const i2 = IntersectCellWithLine(cell, b, c); + if (i2 == CELL_OBJECT_INTERSECT) + return CELL_OBJECT_INTERSECT; + CellObjectIntersection const i3 = IntersectCellWithLine(cell, c, a); + if (i3 == CELL_OBJECT_INTERSECT) + return CELL_OBJECT_INTERSECT; + ASSERT_EQUAL(i1, i2, (cell, a, b, c)); + ASSERT_EQUAL(i2, i3, (cell, a, b, c)); + ASSERT_EQUAL(i3, i1, (cell, a, b, c)); + return i1; } + +template +void CoverObject(IntersectF const & intersect, uint64_t cellPenaltyArea, vector & out, + CellIdT cell = CellIdT::Root()) +{ + uint64_t const cellArea = my::sq(uint64_t(1 << (CellIdT::DEPTH_LEVELS - 1 - cell.Level()))); + CellObjectIntersection const intersection = intersect(cell); + + if (intersection == CELL_OBJECT_NO_INTERSECTION) + return; + if (intersection == CELL_INSIDE_OBJECT || + cell.Level() == CellIdT::DEPTH_LEVELS - 1 || + cellPenaltyArea >= cellArea) + { + out.push_back(cell); + return; + } + + vector subdiv; + for (uint8_t i = 0; i < 4; ++i) + CoverObject(intersect, cellPenaltyArea, subdiv, cell.Child(i)); + + + uint64_t subdivArea = 0; + for (size_t i = 0; i < subdiv.size(); ++i) + subdivArea += my::sq(uint64_t(1 << (CellIdT::DEPTH_LEVELS - 1 - subdiv[i].Level()))); + + ASSERT(!subdiv.empty(), (cellPenaltyArea, out, cell)); + + if (subdiv.empty() || + cellPenaltyArea * (int(subdiv.size()) - 1) >= cellArea - subdivArea) + out.push_back(cell); + else + out.insert(out.end(), subdiv.begin(), subdiv.end()); +} + + +} // namespace covering diff --git a/geometry/covering_stream_optimizer.hpp b/geometry/covering_stream_optimizer.hpp deleted file mode 100644 index c167588751..0000000000 --- a/geometry/covering_stream_optimizer.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once -#include "covering.hpp" -#include "../base/assert.hpp" -#include "../base/logging.hpp" -#include "../std/algorithm.hpp" -#include "../std/deque.hpp" -#include "../std/iterator.hpp" -#include "../std/map.hpp" -#include "../std/utility.hpp" - -namespace covering -{ - -template -class CoveringStreamOptimizer -{ -public: - typedef CellIdT CellId; - - CoveringStreamOptimizer(SinkT & sink, uint32_t windowSize, uint32_t maxDuplicates) - : m_Sink(sink), m_WindowSize(windowSize), m_MaxDuplicates(maxDuplicates), - m_LastPopped(0), m_LastPushed(0) - { - CHECK_GREATER_OR_EQUAL(windowSize, maxDuplicates, ()); - } - - ~CoveringStreamOptimizer() - { - Flush(); - } - - void Add(int64_t id, ValueT value) - { - CHECK_LESS_OR_EQUAL(m_LastPushed, id, (value)); - m_LastPushed = id; - m_Buffer.push_back(make_pair(id, value)); - if (++m_ValueCounts[value] >= m_MaxDuplicates && m_Buffer.size() >= m_WindowSize) - ProcessWindow(value); - while (m_Buffer.size() >= m_WindowSize) - PopFront(); - } - - void Flush() - { - while (!m_Buffer.empty()) - PopFront(); - } - - static void Optimize(vector & ids, int64_t minId) - { - CHECK_GREATER(ids.size(), 2, (minId, ids)); - CHECK(IsSortedAndUnique(ids.begin(), ids.end()), (minId, ids)); - CHECK_GREATER_OR_EQUAL(ids[0], minId, (minId, ids)); - - vector cells(ids.size(), CellId("")); - for (size_t i = 0; i < ids.size(); ++i) - cells[i] = CellId::FromInt64(ids[i]); - covering::Covering covering(cells, minId); - covering.Simplify(minId); - - vector res; - covering.OutputToVector(res); - sort(res.begin(), res.end()); - - CHECK(IsSortedAndUnique(res.begin(), res.end()), (minId, ids, res)); - CHECK_GREATER_OR_EQUAL(res[0], minId, (minId, ids, res)); - - ids.swap(res); - } - -private: - void PopFront() - { - CHECK_LESS_OR_EQUAL(m_LastPopped, m_Buffer.front().first, ()); - m_LastPopped = m_Buffer.front().first; - m_Sink(m_Buffer.front().first, m_Buffer.front().second); - typename map::iterator it = m_ValueCounts.find(m_Buffer.front().second); - CHECK(it != m_ValueCounts.end(), (m_Buffer.front().second)) - CHECK_GREATER(it->second, 0, (m_Buffer.front().second)); - if (--(it->second) == 0) - m_ValueCounts.erase(it); - m_Buffer.pop_front(); - } - - void ProcessWindow(ValueT value) - { - CHECK(IsSortedAndUnique(m_Buffer.begin(), m_Buffer.end()), ()); - vector > others; - vector ids; - for (typename BufferType::const_iterator it = m_Buffer.begin(); it != m_Buffer.end(); ++it) - { - if (it->second == value) - ids.push_back(it->first); - else - others.push_back(*it); - } - CHECK_GREATER_OR_EQUAL(ids.size(), m_MaxDuplicates, ()); - Optimize(ids, m_LastPopped); - CHECK(IsSortedAndUnique(ids.begin(), ids.end()), (ids)); - vector > optimized(ids.size(), make_pair(0LL, value)); - for (size_t i = 0; i < ids.size(); ++i) - optimized[i].first = ids[i]; - CHECK(IsSortedAndUnique(others.begin(), others.end()), (others)); - CHECK(IsSortedAndUnique(optimized.begin(), optimized.end()), (optimized)); - m_Buffer.clear(); - set_union(others.begin(), others.end(), - optimized.begin(), optimized.end(), - back_inserter(m_Buffer)); - m_ValueCounts[value] = optimized.size(); - } - - SinkT & m_Sink; - uint32_t const m_WindowSize; - uint32_t const m_MaxDuplicates; - typedef deque > BufferType; - BufferType m_Buffer; - map m_ValueCounts; - int64_t m_LastPopped, m_LastPushed; -}; - -} diff --git a/geometry/geometry.pro b/geometry/geometry.pro index ede136c5d9..345ae13e50 100644 --- a/geometry/geometry.pro +++ b/geometry/geometry.pro @@ -23,7 +23,6 @@ HEADERS += \ cellid.hpp \ rect_intersect.hpp \ covering.hpp \ - covering_stream_optimizer.hpp \ packer.hpp \ pointu_to_uint64.hpp \ simplification.hpp \ diff --git a/geometry/geometry_tests/covering_stream_optimizer_test.cpp b/geometry/geometry_tests/covering_stream_optimizer_test.cpp deleted file mode 100644 index 5df567c940..0000000000 --- a/geometry/geometry_tests/covering_stream_optimizer_test.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "../../testing/testing.hpp" -#include "../cellid.hpp" -#include "../covering.hpp" -#include "../covering_stream_optimizer.hpp" -#include "../../base/macros.hpp" - -typedef m2::CellId<5> CellId; - -namespace -{ - struct TestSink - { - vector > & m_Res; - - TestSink(vector > & res) : m_Res(res) {} - - void operator() (int64_t cell, int value) const - { - m_Res.push_back(make_pair(cell, value)); - } - }; -} - -UNIT_TEST(CoveringStreamOptimizer_Smoke) -{ - // my::g_LogLevel = LDEBUG; - vector > res1; - TestSink sink(res1); - covering::CoveringStreamOptimizer optimizer(sink, 5, 4); - optimizer.Add(CellId("01").ToInt64(), 1); - optimizer.Add(CellId("01").ToInt64(), 2); - optimizer.Add(CellId("0200").ToInt64(), 1); - optimizer.Add(CellId("0201").ToInt64(), 1); - optimizer.Add(CellId("0202").ToInt64(), 1); - optimizer.Add(CellId("021").ToInt64(), 1); - optimizer.Add(CellId("022").ToInt64(), 1); - optimizer.Flush(); - vector > e, res; - for (size_t i = 0; i < res1.size(); ++i) - res.push_back(make_pair(CellId::FromInt64(res1[i].first), res1[i].second)); - e.push_back(make_pair(CellId("01"), 1)); - e.push_back(make_pair(CellId("01"), 2)); - e.push_back(make_pair(CellId("02"), 1)); - TEST_EQUAL(e, res, ()); -} - -UNIT_TEST(CoveringStreamOptimizer_MinId1) -{ - int64_t const minId = 72200890973LL; - int64_t ids[] = { 72200890973, 72200890995, 72200891005, 72200891011, 72200891013, - 72200891014, 72200891015, 72200891036, 72200896894, 72200896902 }; - vector idsV(&ids[0], &ids[0] + ARRAY_SIZE(ids)); - covering::CoveringStreamOptimizer, int, TestSink>::Optimize(idsV, minId); -} - -UNIT_TEST(CoveringStreamOptimizer_MinId2) -{ - int64_t const minId = 72200890973LL; - int64_t ids[] = { 72200890973, 72200890994, 72200891015, 72200891036}; - vector idsV(&ids[0], &ids[0] + ARRAY_SIZE(ids)); - covering::CoveringStreamOptimizer, int, TestSink>::Optimize(idsV, minId); -} - diff --git a/geometry/geometry_tests/covering_test.cpp b/geometry/geometry_tests/covering_test.cpp index 5f41c0eff8..d69e4899c6 100644 --- a/geometry/geometry_tests/covering_test.cpp +++ b/geometry/geometry_tests/covering_test.cpp @@ -1,127 +1,4 @@ #include "../../testing/testing.hpp" -#include "../cellid.hpp" #include "../covering.hpp" -#include "../point2d.hpp" -#include "../../base/stl_add.hpp" -typedef m2::CellId<5> CellId; - -UNIT_TEST(CoverTriangle_Simple) -{ - vector v; - covering::Covering c(m2::PointD(3*2, 3*2), m2::PointD(4*2, 12*2), m2::PointD(14*2, 3*2)); - c.OutputToVector(v); - vector e; - e.push_back(CellId("03")); - e.push_back(CellId("003")); - e.push_back(CellId("012")); - e.push_back(CellId("013")); - e.push_back(CellId("102")); - e.push_back(CellId("103")); - e.push_back(CellId("112")); - e.push_back(CellId("120")); - e.push_back(CellId("121")); - e.push_back(CellId("122")); - e.push_back(CellId("210")); - e.push_back(CellId("211")); - e.push_back(CellId("212")); - e.push_back(CellId("0211")); - e.push_back(CellId("0213")); - e.push_back(CellId("0231")); - e.push_back(CellId("0233")); - e.push_back(CellId("1130")); - e.push_back(CellId("1132")); - e.push_back(CellId("1230")); - e.push_back(CellId("1300")); - e.push_back(CellId("2011")); - e.push_back(CellId("2013")); - e.push_back(CellId("2031")); - e.push_back(CellId("2033")); - e.push_back(CellId("2130")); - e.push_back(CellId("2211")); - e.push_back(CellId("2300")); - e.push_back(CellId("3000")); - TEST_EQUAL(v, e, ()); -} - -UNIT_TEST(CoverTriangle_TriangleInsideCell) -{ - vector v; - covering::Covering c(m2::PointD(0.1, 0.1), m2::PointD(0.2, 0.2), m2::PointD(0.2, 0.4)); - c.OutputToVector(v); - vector e; - e.push_back(CellId("0000")); - TEST_EQUAL(v, e, ()); -} - -UNIT_TEST(Covering_Append_Simple) -{ - vector v1, v2, v3; - - v1.push_back(CellId("012")); - v2.push_back(CellId("0123")); - v3.push_back(CellId("012")); - - v1.push_back(CellId("023")); - v2.push_back(CellId("023")); - v3.push_back(CellId("023")); - - v1.push_back(CellId("130")); - v1.push_back(CellId("131")); - v2.push_back(CellId("133")); - v1.push_back(CellId("1320")); - v1.push_back(CellId("1321")); - v2.push_back(CellId("1322")); - v2.push_back(CellId("1323")); - v3.push_back(CellId("13")); - - covering::Covering c1(v1); - c1.Append(covering::Covering(v2)); - vector v4; - c1.OutputToVector(v4); - sort(v4.begin(), v4.end(), CellId::LessStackOrder()); - TEST_EQUAL(v3, v4, ()); -} - -UNIT_TEST(IntersectCellWithTriangle_EmptyTriangle) -{ - m2::PointU pt(27, 31); - TEST_EQUAL(covering::CELL_TRIANGLE_NO_INTERSECTION, - covering::IntersectCellWithTriangle(CellId("0"), pt, pt, pt), ()); - TEST_EQUAL(covering::CELL_TRIANGLE_NO_INTERSECTION, - covering::IntersectCellWithTriangle(CellId("1"), pt, pt, pt), ()); - TEST_EQUAL(covering::CELL_TRIANGLE_NO_INTERSECTION, - covering::IntersectCellWithTriangle(CellId("2"), pt, pt, pt), ()); - TEST_EQUAL(covering::TRIANGLE_INSIDE_CELL, - covering::IntersectCellWithTriangle(CellId("3"), pt, pt, pt), ()); -} - -UNIT_TEST(Covering_EmptyTriangle) -{ - m2::PointU pt(27, 31); - CellId const expectedCellId = CellId::FromXY(pt.x, pt.y); - TEST_GREATER(expectedCellId.ToInt64(), 5, ()); - covering::Covering covering(pt, pt, pt); - vector ids; - covering.OutputToVector(ids); - TEST_EQUAL(ids, vector(1, expectedCellId), ()); -} - -UNIT_TEST(Covering_Simplify_Smoke) -{ - vector v; - v.push_back(CellId("03")); - v.push_back(CellId("020")); - v.push_back(CellId("021")); - v.push_back(CellId("022")); - v.push_back(CellId("0012")); - covering::Covering covering(v); - v.clear(); - covering.Simplify(); - covering.OutputToVector(v); - vector e; - e.push_back(CellId("02")); - e.push_back(CellId("03")); - e.push_back(CellId("0012")); - TEST_EQUAL(v, e, ()); -} +// TODO: Add covering unit tests here. diff --git a/geometry/geometry_tests/geometry_tests.pro b/geometry/geometry_tests/geometry_tests.pro index 86f598ecd5..51bc49822b 100644 --- a/geometry/geometry_tests/geometry_tests.pro +++ b/geometry/geometry_tests/geometry_tests.pro @@ -26,7 +26,6 @@ SOURCES += \ packer_test.cpp \ segments_intersect_test.cpp \ covering_test.cpp \ - covering_stream_optimizer_test.cpp \ pointu_to_uint64_test.cpp \ simplification_test.cpp \ transformations_test.cpp \ diff --git a/indexer/cell_coverer.hpp b/indexer/cell_coverer.hpp index 2f575493c2..0d8aec8ef1 100644 --- a/indexer/cell_coverer.hpp +++ b/indexer/cell_coverer.hpp @@ -5,126 +5,7 @@ #include "../std/queue.hpp" #include "../std/vector.hpp" -inline bool IntersectsHoriz(CoordT x1, CoordT y1, CoordT x2, CoordT y2, - CoordT y, CoordT l, CoordT r) -{ - CoordT d = (y1 - y) * (y2 - y); - if (d > 0) return false; - - CoordT x = x1 + (x2 - x1) * (y - y1) / (y2 - y1); - - if ((l - x) * (r - x) <= 0) return true; else return false; -} - -inline bool IntersectsVert(CoordT x1, CoordT y1, CoordT x2, CoordT y2, - CoordT x, CoordT b, CoordT t) -{ - CoordT d = (x1 - x) * (x2 - x); - if (d > 0) return false; - - CoordT y = y1 + (y2 - y1) * (x - x1) / (x2 - x1); - - if ((b - y) * (t - y) <= 0) return true; else return false; -} - -template -inline bool CellIntersects(vector const & polyLine, CellIdT id) -{ - CoordT minX, minY, maxX, maxY; - CellIdConverter::GetCellBounds(id, minX, minY, maxX, maxY); - CoordPointT minPoint = make_pair(minX, minY); - CoordPointT maxPoint = make_pair(maxX, maxY); - - for (size_t i = 0; i < polyLine.size() - 1; ++i) - { - if (IntersectsHoriz(polyLine[i].first, polyLine[i].second, - polyLine[i + 1].first, polyLine[i + 1].second, - minPoint.second, minPoint.first, maxPoint.first)) return true; - - if (IntersectsHoriz(polyLine[i].first, polyLine[i].second, - polyLine[i + 1].first, polyLine[i + 1].second, - maxPoint.second, minPoint.first, maxPoint.first)) return true; - - if (IntersectsVert(polyLine[i].first, polyLine[i].second, - polyLine[i + 1].first, polyLine[i + 1].second, - minPoint.first, minPoint.second, maxPoint.second)) return true; - - if (IntersectsVert(polyLine[i].first, polyLine[i].second, - polyLine[i + 1].first, polyLine[i + 1].second, - maxPoint.first, minPoint.second, maxPoint.second)) return true; - } - - return false; -} - -template -inline void SplitCell(vector const & polyLine, queue & cellQueue) -{ - CellIdT id = cellQueue.front(); - cellQueue.pop(); - - for (size_t i = 0; i < 4; ++i) - { - CellIdT child = id.Child(i); - - if (CellIntersects(polyLine, child)) - { - cellQueue.push(child); - } - } -} - -template -inline bool FindBounds(ItT begin, ItT end, - CoordT & minX, CoordT & minY, CoordT & maxX, CoordT & maxY) -{ - if (begin == end) return false; - - minX = begin->first; - maxX = begin->first; - minY = begin->second; - maxY = begin->second; - - for (ItT it = begin; it != end; ++it) - { - if (it->first < minX) minX = it->first; - if (it->first > maxX) maxX = it->first; - if (it->second < minY) minY = it->second; - if (it->second > maxY) maxY = it->second; - } - - return true; -} - -template -inline CellIdT CoverPoint(CoordPointT const & point) -{ - return CellIdConverter::ToCellId(point.first, point.second); -} - -template -inline void CoverPolyLine(vector< CoordPointT > const & polyLine, size_t cellDepth, - vector & cells) -{ - CoordT minX = 0, minY = 0, maxX = 0, maxY = 0; - FindBounds(polyLine.begin(), polyLine.end(), minX, minY, maxX, maxY); - - CellIdT commonCell = - CellIdConverter::Cover2PointsWithCell(minX, minY, maxX, maxY); - - queue cellQueue; - cellQueue.push(commonCell); - while (cellQueue.front().Level() < static_cast(cellDepth)) // cellQueue.size() < cells_count - { - SplitCell(polyLine, cellQueue); - } - - while (!cellQueue.empty()) - { - cells.push_back(cellQueue.front()); - cellQueue.pop(); - } -} +// TODO: Move neccessary functions to geometry/covering.hpp and delete this file. template inline void SplitRectCell(CellIdT id, diff --git a/indexer/covering.cpp b/indexer/covering.cpp index 57e776c290..9d61007bc1 100644 --- a/indexer/covering.cpp +++ b/indexer/covering.cpp @@ -8,90 +8,79 @@ #include "../std/algorithm.hpp" #include "../std/bind.hpp" -// TODO: Redo polyline covering right. - namespace { - template - class TriangleCoverer - { - public: - typedef typename CoveringT::CellId CellId; - typedef CellIdConverter CellIdConverterType; - explicit TriangleCoverer(CoveringT const & covering) : m_Covering(1, covering) {} - - void operator () (m2::PointD const & a, m2::PointD const & b, m2::PointD const & c) - { - AddTriangle(a, b, c); - } - - void AddTriangle(m2::PointD const & a, m2::PointD const & b, m2::PointD const & c) - { - m_Covering.push_back(CoveringT( - m2::PointD(CellIdConverterType::XToCellIdX(a.x), CellIdConverterType::YToCellIdY(a.y)), - m2::PointD(CellIdConverterType::XToCellIdX(b.x), CellIdConverterType::YToCellIdY(b.y)), - m2::PointD(CellIdConverterType::XToCellIdX(c.x), CellIdConverterType::YToCellIdY(c.y)))); - while (m_Covering.size() > 1 && - m_Covering[m_Covering.size() - 2].Size() < m_Covering.back().Size()) - { - m_Covering[m_Covering.size() - 2].Append(m_Covering.back()); - m_Covering.pop_back(); - } - } - - CoveringT const & GetCovering() - { - ASSERT(!m_Covering.empty(), ()); -#ifdef DEBUG - // Make appends in another order and assert that result is the same. - CoveringT dbgCovering(m_Covering[0]); - for (size_t i = 1; i < m_Covering.size(); ++i) - dbgCovering.Append(m_Covering[i]); -#endif - while (m_Covering.size() > 1) - { - m_Covering[m_Covering.size() - 2].Append(m_Covering.back()); - m_Covering.pop_back(); - } -#ifdef DEBUG - vector dbgIds, ids; - dbgCovering.OutputToVector(dbgIds); - m_Covering[0].OutputToVector(ids); - ASSERT_EQUAL(dbgIds, ids, ()); -#endif - return m_Covering[0]; - } - - private: - vector m_Covering; - }; -} - -vector covering::CoverFeature(FeatureType const & f, int level) +class FeatureIntersector { - vector geometry; - f.ForEachPoint(MakeBackInsertFunctor(geometry), level); - - vector ids; - if (!geometry.empty()) +public: + struct Trg { - if (geometry.size() > 1) - { - // TODO: Tweak CoverPolyLine() depth level. - CoverPolyLine(geometry, RectId::DEPTH_LEVELS - 1, ids); - } - else - ids.push_back(CoverPoint(geometry[0])); + m2::PointD m_A, m_B, m_C; + Trg(m2::PointU a, m2::PointU b, m2::PointU c) : m_A(a), m_B(b), m_C(c) {} + }; + vector m_Polyline; + vector m_Trg; + + covering::CellObjectIntersection operator() (RectId const & cell) const + { + for (size_t i = 1; i < m_Polyline.size(); ++i) + if (covering::IntersectCellWithLine(cell, m_Polyline[i], m_Polyline[i-1])) + return covering::CELL_OBJECT_INTERSECT; + for (size_t i = 0; i < m_Trg.size(); ++i) + if (covering::CellObjectIntersection res = + covering::IntersectCellWithTriangle(cell, m_Trg[i].m_A, m_Trg[i].m_B, m_Trg[i].m_C)) + return res == covering::OBJECT_INSIDE_CELL ? covering::CELL_OBJECT_INTERSECT : res; + return covering::CELL_OBJECT_NO_INTERSECTION; } - typedef covering::Covering CoveringType; - typedef TriangleCoverer CovererType; - CovererType coverer = CovererType(CoveringType(ids)); - f.ForEachTriangleRef(coverer, level); + typedef CellIdConverter CellIdConverterType; + + static m2::PointD ConvertPoint(double x, double y) + { + return m2::PointD(CellIdConverterType::XToCellIdX(x), + CellIdConverterType::YToCellIdY(y)); + } + + void operator() (pair const & pt) + { + m_Polyline.push_back(ConvertPoint(pt.first, pt.second)); + } + + void operator() (m2::PointD const & a, m2::PointD const & b, m2::PointD const & c) + { + m_Trg.push_back(Trg(ConvertPoint(a.x, a.y), + ConvertPoint(b.x, b.y), + ConvertPoint(c.x, c.y))); + } +}; + +} + +vector covering::CoverFeature(FeatureType const & f, + uint64_t cellPenaltyAreaPerNode) +{ + FeatureIntersector featureIntersector; + f.ForEachPointRef(featureIntersector, -1); + f.ForEachTriangleRef(featureIntersector, -1); + + CHECK(!featureIntersector.m_Trg.empty() || !featureIntersector.m_Polyline.empty(), \ + (f.DebugString(-1))); + + if (featureIntersector.m_Trg.empty() && featureIntersector.m_Polyline.size() == 1) + { + m2::PointD pt = featureIntersector.m_Polyline[0]; + return vector( + 1, RectId::FromXY(static_cast(pt.x), static_cast(pt.y)).ToInt64()); + } + + vector cells; + covering::CoverObject(featureIntersector, cellPenaltyAreaPerNode, cells); + + vector res(cells.size()); + for (size_t i = 0; i < cells.size(); ++i) + res[i] = cells[i].ToInt64(); - vector res; - coverer.GetCovering().OutputToVector(res); return res; } diff --git a/indexer/covering.hpp b/indexer/covering.hpp index 54452194c8..2c650821fe 100644 --- a/indexer/covering.hpp +++ b/indexer/covering.hpp @@ -12,7 +12,8 @@ class FeatureType; namespace covering { // Cover feature with RectIds and return their integer representations. - vector CoverFeature(FeatureType const & feature, int level); + vector CoverFeature(FeatureType const & feature, + uint64_t cellPenaltyAreaPerNode); // Cover viewport with RectIds and append their RectIds as well. vector > CoverViewportAndAppendLowerLevels(m2::RectD const & rect); // Given a vector of intervals [a, b), sort them and merge overlapping intervals. diff --git a/indexer/indexer_tests/cell_coverer_test.cpp b/indexer/indexer_tests/cell_coverer_test.cpp index fc01826b07..94d2f98475 100644 --- a/indexer/indexer_tests/cell_coverer_test.cpp +++ b/indexer/indexer_tests/cell_coverer_test.cpp @@ -5,97 +5,16 @@ #include "../../coding/hex.hpp" #include "../../base/logging.hpp" - // Unit test uses m2::CellId<30> for historical reasons, the actual production code uses RectId. typedef m2::CellId<30> CellIdT; typedef Bounds<-180, -90, 180, 90> OrthoBounds; -namespace -{ - class CoordsPusher - { - public: - typedef vector< CoordPointT > VT; - - CoordsPusher(VT & v) : m_v(v) {} - CoordsPusher & operator()(CoordT x, CoordT y) - { - m_v.push_back(make_pair(x, y)); - return *this; - } - private: - VT & m_v; - }; - - string EnumCells(vector const & v) - { - string result; - for (size_t i = 0; i < v.size(); ++i) - { - result += v[i].ToString(); - if (i != v.size() - 1) result += ' '; - } - return result; - } -} - UNIT_TEST(CellIdToStringRecode) { char const kTest[] = "21032012203"; TEST_EQUAL(CellIdT::FromString(kTest).ToString(), kTest, ()); } -UNIT_TEST(GoldenTestCover) -{ - vector coords; - CoordsPusher c(coords); - c(0.7, 0.5) - (1.5, 1.5) - (2.5, 3.5) - (5.5, 5.0); - - vector cells; - CoverPolyLine >(coords, 3, cells); - - TEST_EQUAL(EnumCells(cells), "000 001 003 021 030 032 033 211 300 301 303", ()); -} - -UNIT_TEST(GoldenTestCellIntersect) -{ - vector< CoordPointT > coords; - CoordsPusher c(coords); - c (0.7, 0.5) - (1.5, 1.5) - (2.5, 3.5) - (5.5, 5.0); - - vector cells; - - typedef Bounds<0, 0, 8, 8> BoundsT; - - CoverPolyLine(coords, 7, cells); - TEST(!CellIntersects(coords, CellIdT::FromString("210")), ()); -} - -UNIT_TEST(GoldenOrthoCover) -{ - vector< CoordPointT > coords; - CoordsPusher c(coords); - c - (27.545927047729492, 53.888893127441406) - (27.546476364135742, 53.888614654541016) - (27.546852111816406, 53.889347076416016) - (27.546596527099609, 53.889404296875000) - (27.545927047729492, 53.888893127441406); - - vector cells; - CoverPolyLine(coords, 19, cells); - - TEST_EQUAL(EnumCells(cells), - "3201221130210310103 3201221130210310120 3201221130210310121 3201221130210310122 " - "3201221130210310123 3201221130210310301 3201221130210310310", ()); -} - UNIT_TEST(GoldenCoverRect) { vector cells; @@ -123,40 +42,3 @@ UNIT_TEST(ArtificialCoverRect) TEST_EQUAL(cells[2].ToString(), "21", ()); TEST_EQUAL(cells[3].ToString(), "30", ()); } - -UNIT_TEST(CoverEmptyTriangleTest) -{ - vector ids, expectedIds; - m2::PointD pt(8.89748, 51.974); - expectedIds.push_back(CoverPoint(CoordPointT(pt.x, pt.y)).ToInt64()); - TEST_NOT_EQUAL(expectedIds[0], 1, ()); - typedef CellIdConverter CellIdConverterType; - m2::PointD pt1(CellIdConverterType::XToCellIdX(pt.x), CellIdConverterType::YToCellIdY(pt.y)); - covering::Covering covering(pt1, pt1, pt1); - covering.OutputToVector(ids); - TEST_EQUAL(ids, expectedIds, ()); -} - -// TODO: UNIT_TEST(CoverPolygon) -/* -UNIT_TEST(CoverPolygon) -{ - typedef Bounds<0, 0, 16, 16> TestBounds; - - vector< CoordPointT > coords; - CoordsPusher c(coords); - c - (4.1, 4.1) - (6.1, 8.1) - (10.1, 10.1) - (8.1, 6.1) - (4.1, 4.1); - - vector cells; - CoverPolygon(coords, 4, cells); - - TEST_EQUAL(EnumCells(cells), - "0300 0301 0302 0303 0312 0313 0321 0330 0331 1220 0323 0332 0333 " - "1222 1223 2110 2111 3000 3001 2113 3002 3003 3012 3021 3030", ()); -} -*/ diff --git a/indexer/indexer_tests/index_builder_test.cpp b/indexer/indexer_tests/index_builder_test.cpp index e1e28568df..ea81177eb3 100644 --- a/indexer/indexer_tests/index_builder_test.cpp +++ b/indexer/indexer_tests/index_builder_test.cpp @@ -24,7 +24,8 @@ UNIT_TEST(BuildIndexTest) { FeaturesVector featuresVector(originalContainer); MemWriter > serialWriter(serialIndex); - indexer::BuildIndex(ScaleIndexBase::NUM_BUCKETS, featuresVector, serialWriter, "build_index_test"); + indexer::BuildIndex(ScaleIndexBase::NUM_BUCKETS, featuresVector, serialWriter, + "build_index_test"); } // Create a new mwm file. diff --git a/indexer/scale_index_builder.hpp b/indexer/scale_index_builder.hpp index 43f36fd73a..0708cd38f1 100644 --- a/indexer/scale_index_builder.hpp +++ b/indexer/scale_index_builder.hpp @@ -5,8 +5,6 @@ #include "interval_index_builder.hpp" #include "cell_id.hpp" -#include "../geometry/covering_stream_optimizer.hpp" - #include "../coding/dd_vector.hpp" #include "../coding/file_sort.hpp" #include "../coding/var_serial_vector.hpp" @@ -71,7 +69,7 @@ public: { if (FeatureShouldBeIndexed(f)) { - vector const cells = covering::CoverFeature(f, GetGeometryScale()); + vector const cells = covering::CoverFeature(f, 1025); for (vector::const_iterator it = cells.begin(); it != cells.end(); ++it) m_Sorter.Add(CellFeaturePair(*it, offset)); ++m_NumFeatures; @@ -112,31 +110,6 @@ private: SinkT & m_Sink; }; -template -class FeatureCoveringOptimizeProxySink -{ -public: - FeatureCoveringOptimizeProxySink(SinkT & sink) - : m_Sink(sink), m_Optimizer(m_Sink, 100, 10) - { - } - - ~FeatureCoveringOptimizeProxySink() - { - m_Optimizer.Flush(); - } - - void operator () (CellFeaturePair const & cellFeaturePair) - { - m_Optimizer.Add(cellFeaturePair.GetCell(), cellFeaturePair.GetFeature()); - } - -private: - CellFeaturePairSinkAdapter m_Sink; - covering::CoveringStreamOptimizer > - m_Optimizer; -}; - template inline void IndexScales(uint32_t bucketsCount, FeaturesVectorT const & featuresVector, @@ -156,16 +129,11 @@ inline void IndexScales(uint32_t bucketsCount, { FileWriter cellsToFeaturesWriter(tmpFilePrefix + ".c2f.sorted"); - typedef FeatureCoveringOptimizeProxySink OptimizeSink; - OptimizeSink optimizeSink(cellsToFeaturesWriter); - typedef FileSorter SorterType; - SorterType sorter(1024*1024, tmpFilePrefix + ".c2f.tmp", optimizeSink); - /* typedef FileSorter > SorterType; WriterFunctor out(cellsToFeaturesWriter); SorterType sorter(1024*1024, tmpFilePrefix + ".c2f.tmp", out); - */ - featuresVector.ForEachOffset(FeatureCoverer(bucket, sorter, numFeatures)); + featuresVector.ForEachOffset( + FeatureCoverer(bucket, sorter, numFeatures)); // LOG(LINFO, ("Sorting...")); sorter.SortAndFinish(); }