diff --git a/base/stl_helpers.hpp b/base/stl_helpers.hpp index ad3376e642..0cef78475a 100644 --- a/base/stl_helpers.hpp +++ b/base/stl_helpers.hpp @@ -442,21 +442,21 @@ struct EnumClassHash } }; -struct RetrieveKey +struct RetrieveFirst { - template - typename T::first_type const & operator()(T const & keyValue) const - { - return keyValue.first; - } + template + typename T::first_type const & operator()(T const & pair) const + { + return pair.first; + } }; -struct RetrieveValue +struct RetrieveSecond { - template - typename T::second_type const & operator()(T const & keyValue) const - { - return keyValue.second; - } + template + typename T::second_type const & operator()(T const & pair) const + { + return pair.second; + } }; } // namespace base diff --git a/generator/final_processor_intermediate_mwm.cpp b/generator/final_processor_intermediate_mwm.cpp index 2fb8b6e2f0..709f9a905c 100644 --- a/generator/final_processor_intermediate_mwm.cpp +++ b/generator/final_processor_intermediate_mwm.cpp @@ -220,7 +220,7 @@ public: std::vector fbs; fbs.reserve(fbsWithIds.size()); std::transform(std::cbegin(fbsWithIds), std::cend(fbsWithIds), - std::back_inserter(fbs), base::RetrieveKey()); + std::back_inserter(fbs), base::RetrieveFirst()); auto const affiliations = GetAffiliations(fbs, m_affiliation, m_threadsCount); AppendToCountries(fbs, affiliations, m_temporaryMwmPath, m_threadsCount); diff --git a/generator/generator_tests/CMakeLists.txt b/generator/generator_tests/CMakeLists.txt index 6afc5da24a..6f4e37d47e 100644 --- a/generator/generator_tests/CMakeLists.txt +++ b/generator/generator_tests/CMakeLists.txt @@ -9,6 +9,7 @@ set( cities_boundaries_checker_tests.cpp cities_ids_tests.cpp city_roads_tests.cpp + cluster_finder_tests.cpp coasts_test.cpp collector_city_area_tests.cpp common.cpp diff --git a/generator/generator_tests/cluster_finder_tests.cpp b/generator/generator_tests/cluster_finder_tests.cpp new file mode 100644 index 0000000000..fa0d909ef7 --- /dev/null +++ b/generator/generator_tests/cluster_finder_tests.cpp @@ -0,0 +1,146 @@ +#include "testing/testing.hpp" + +#include "generator/place_processor.hpp" + +#include "geometry/mercator.hpp" +#include "geometry/point2d.hpp" +#include "geometry/rect2d.hpp" + +#include "base/assert.hpp" + +#include +#include +#include +#include + +namespace +{ +using namespace generator; + +enum class Type { T1 = 1, T2, T3 }; + +std::string DebugPrint(Type const & t) +{ + return "T" + std::to_string(static_cast(t)); +} + +struct NamedPoint +{ + NamedPoint(m2::PointD const & point, Type const & type, std::string const & name) + : m_point(point), m_type(type), m_name(name) {} + + size_t m_id = m_counter++; + m2::PointD m_point; + Type m_type; + std::string m_name; + +private: + static size_t m_counter; +}; + +size_t NamedPoint::m_counter = 0; + +m2::RectD GetLimitRect(NamedPoint const & p) +{ + return m2::RectD(p.m_point, p.m_point); +} + +bool operator==(NamedPoint const & left, NamedPoint const & right) +{ + return left.m_id == right.m_id; +} + +std::string DebugPrint(NamedPoint const & t) +{ + return DebugPrint(t.m_point) + " " + std::to_string(static_cast(t.m_type)) + " " + t.m_name; +} + +auto const getRadiusMFunction = [](NamedPoint const & p) { + switch (p.m_type) { + case Type::T1: return 4000; + case Type::T2: return 8000; + case Type::T3: return 16000; + }; + UNREACHABLE(); +}; + +auto const isSameFunction = [](NamedPoint const & left, NamedPoint const & right) { + if (left.m_name != right.m_name) + return false; + + if (left.m_type != right.m_type) + return false; + + return MercatorBounds::DistanceOnEarth(left.m_point, right.m_point) < getRadiusMFunction(left); +}; + +template class Container, typename Alloc = std::allocator> +std::vector> GetClusters( + Container && container, + typename ClustersFinder::RadiusFunc const & radiusFunc, + typename ClustersFinder::IsSameFunc const & isSameFunc) +{ + return ClustersFinder(std::forward>(container), + radiusFunc, isSameFunc).Find(); +} + +UNIT_TEST(ClustersFinder_Empty) +{ + std::list emptyList; + TEST_EQUAL(GetClusters(std::move(emptyList), getRadiusMFunction, isSameFunction), + std::vector>(), ()); +} + +UNIT_TEST(ClustersFinder_OneElement) +{ + NamedPoint p1({0.0, 0.0}, Type::T1, "name"); + std::list l{p1}; + std::vector> expected{{p1}}; + TEST_EQUAL(GetClusters(std::move(l), getRadiusMFunction, isSameFunction), expected, ()); +} + +UNIT_TEST(ClustersFinder_TwoElements) +{ + NamedPoint p1({0.0, 0.0}, Type::T1, "name"); + NamedPoint p2({0.0001, 0.0001}, Type::T1, "name"); + std::list l{p1, p2}; + + std::vector> expected{{p1, p2}}; + TEST_EQUAL(GetClusters(std::move(l), getRadiusMFunction, isSameFunction), expected, ()); +} + +UNIT_TEST(ClustersFinder_TwoClusters) +{ + { + NamedPoint p1({0.0, 0.0}, Type::T1, "name1"); + NamedPoint p2({0.0001, 0.0001}, Type::T1, "name2"); + std::list l{p1, p2}; + std::vector> expected{{p2}, {p1}}; + TEST_EQUAL(GetClusters(std::move(l), getRadiusMFunction, isSameFunction), expected, ()); + } + { + NamedPoint p1({0.0, 0.0}, Type::T1, "name"); + NamedPoint p2({0.1, 0.1}, Type::T1, "name"); + std::list l{p1, p2}; + + std::vector> expected{{p1}, {p2}}; + TEST_EQUAL(GetClusters(std::move(l), getRadiusMFunction, isSameFunction), expected, ()); + } +} + +UNIT_TEST(ClustersFinder_ThreeClusters) +{ + NamedPoint p1({0.0, 0.0}, Type::T1, "name"); + NamedPoint p2({0.0, 0.00001}, Type::T1, "name"); + NamedPoint p3({0.0001, 0.0000}, Type::T1, "name"); + + NamedPoint p11({0.0, 0.0}, Type::T2, "name"); + NamedPoint p12({0.0, 0.001}, Type::T2, "name"); + NamedPoint p13({0.001, 0.0000}, Type::T2, "name"); + + NamedPoint p21({0.0, 0.0}, Type::T1, "name21"); + std::list l{p1, p2, p3, p11, p12, p13, p21}; + std::vector> expected{{p2, p1, p3}, {p11, p13, p12}, {p21}}; + TEST_EQUAL(GetClusters(std::move(l), getRadiusMFunction, isSameFunction), expected, ()); +} +} // namespace diff --git a/generator/generator_tests/place_processor_tests.cpp b/generator/generator_tests/place_processor_tests.cpp index 771dac7c9b..d9fbd52843 100644 --- a/generator/generator_tests/place_processor_tests.cpp +++ b/generator/generator_tests/place_processor_tests.cpp @@ -120,11 +120,11 @@ void Sort(std::vector & value) } void Test(std::vector value, - std::vector excpected) + std::vector expected) { Sort(value); - Sort(excpected); - TEST_EQUAL(value, excpected, ()); + Sort(expected); + TEST_EQUAL(value, expected, ()); } } // namespace diff --git a/generator/place_processor.cpp b/generator/place_processor.cpp index d9f2895c09..6bb78d0f33 100644 --- a/generator/place_processor.cpp +++ b/generator/place_processor.cpp @@ -6,12 +6,9 @@ #include "indexer/classificator.hpp" #include "indexer/ftypes_matcher.hpp" -#include "geometry/mercator.hpp" - #include "base/assert.hpp" #include -#include #include #include @@ -29,7 +26,7 @@ namespace { using namespace generator; -double GetThresholdM(ftypes::Type const & type) +double GetRadiusM(ftypes::Type const & type) { switch (type) { @@ -78,18 +75,19 @@ bool IsTheSamePlace(T const & left, T const & right) return true; auto const dist = MercatorBounds::DistanceOnEarth(left.GetKeyPoint(), right.GetKeyPoint()); - return dist <= GetThresholdM(localityL); + return dist <= GetRadiusM(localityL); } std::vector> FindClusters(std::vector && places) { - auto func = [](FeaturePlace const & fp) { + auto const func = [](FeaturePlace const & fp) { auto const & localityChecker = ftypes::IsLocalityChecker::Instance(); auto const locality = localityChecker.GetType(GetPlaceType(fp.GetFb())); - return GetThresholdM(locality); + return GetRadiusM(locality); }; - ClustersFinder f(std::move(places), func, IsTheSamePlace); - return f.Find(); + ClustersFinder finder(std::move(places), func, + IsTheSamePlace); + return finder.Find(); } } // namespace @@ -157,6 +155,11 @@ bool FeaturePlace::IsPoint() const return GetFb().IsPoint(); } +m2::RectD GetLimitRect(FeaturePlace const & fp) +{ + return fp.GetLimitRect(); +} + PlaceProcessor::PlaceProcessor(std::shared_ptr boundariesTable) : m_boundariesTable(boundariesTable) {} diff --git a/generator/place_processor.hpp b/generator/place_processor.hpp index 4dbdf48517..478cbc3252 100644 --- a/generator/place_processor.hpp +++ b/generator/place_processor.hpp @@ -14,26 +14,30 @@ #include #include #include +#include #include namespace generator { +// The class ClustersFinder finds clusters of objects for which IsSameFunc will return the true. +// RadiusFunc should return the same radius for all objects in one cluster. template class Container, typename Alloc = std::allocator> class ClustersFinder { public: - using DistanceFunc = std::function; + using RadiusFunc = std::function; using IsSameFunc = std::function; using ConstIterator = T const *; - ClustersFinder(Container && container, DistanceFunc distanceFunc, IsSameFunc isSameFunc) - : m_container(std::move(container)), m_distanseFunc(distanceFunc), m_isSameFunc(isSameFunc) + ClustersFinder(Container && container, RadiusFunc const & radiusFunc, + IsSameFunc const & isSameFunc) + : m_container(std::move(container)), m_radiusFunc(radiusFunc), m_isSameFunc(isSameFunc) { for (auto const & e : m_container) m_tree.Add(&e); } - std::vector> Find() + std::vector> Find() const { std::vector> clusters; std::set unviewed; @@ -49,10 +53,10 @@ public: private: struct TraitsDef { - m2::RectD const LimitRect(ConstIterator const & it) const { return it->GetLimitRect(); } + m2::RectD const LimitRect(ConstIterator const & it) const { return GetLimitRect(*it); } }; - std::vector FindOneCluster(ConstIterator const & it, std::set & unviewed) + std::vector FindOneCluster(ConstIterator const & it, std::set & unviewed) const { std::vector cluster{*it}; std::queue queue; @@ -63,24 +67,24 @@ private: auto const current = queue.front(); queue.pop(); auto const queryBbox = GetBboxFor(current); - m_tree.ForEachInRect(queryBbox, [&](auto const & conditate) { - if (current == conditate || unviewed.count(conditate) == 0 || !m_isSameFunc(*current, *conditate)) + m_tree.ForEachInRect(queryBbox, [&](auto const & candidate) { + if (unviewed.count(candidate) == 0 || !m_isSameFunc(*current, *candidate)) return; - unviewed.erase(conditate); - queue.emplace(conditate); - cluster.emplace_back(*conditate); + unviewed.erase(candidate); + queue.emplace(candidate); + cluster.emplace_back(*candidate); }); } return cluster; } - m2::RectD GetBboxFor(ConstIterator const & it) + m2::RectD GetBboxFor(ConstIterator const & it) const { m2::RectD bbox; - it->GetLimitRect().ForEachCorner([&](auto const & p) { - auto const dist = m_distanseFunc(*it); + auto const dist = m_radiusFunc(*it); + GetLimitRect(*it).ForEachCorner([&](auto const & p) { bbox.Add(MercatorBounds::RectByCenterXYAndSizeInMeters(p, dist)); }); @@ -88,7 +92,7 @@ private: } Container m_container; - DistanceFunc m_distanseFunc; + RadiusFunc m_radiusFunc; IsSameFunc m_isSameFunc; m4::Tree m_tree; }; @@ -119,6 +123,8 @@ private: size_t m_bestIndex; }; +m2::RectD GetLimitRect(FeaturePlace const & fp); + // The class PlaceProcessor is responsible for the union of boundaries of the places. class PlaceProcessor {