Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
159
libs/kml/kml_tests/minzoom_quadtree_tests.cpp
Normal file
159
libs/kml/kml_tests/minzoom_quadtree_tests.cpp
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "kml/minzoom_quadtree.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
double constexpr kEps = std::numeric_limits<double>::epsilon();
|
||||
|
||||
m2::PointD MakeGlobalPoint(double x, double y)
|
||||
{
|
||||
m2::PointD point;
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
point.x *= mercator::Bounds::kRangeX;
|
||||
point.y *= mercator::Bounds::kRangeY;
|
||||
point.x += mercator::Bounds::kMinX;
|
||||
point.y += mercator::Bounds::kMinY;
|
||||
return point;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(Kml_MinzoomQuadtree_PopulationGrowthRate)
|
||||
{
|
||||
{
|
||||
kml::MinZoomQuadtree<double /* rank */, std::less<double>> minZoomQuadtree{{} /* less */};
|
||||
size_t const kMaxDepth = 5;
|
||||
double const step = 1.0 / (1 << kMaxDepth);
|
||||
for (int x = 0; x < (1 << kMaxDepth); ++x)
|
||||
for (int y = 0; y < (1 << kMaxDepth); ++y)
|
||||
minZoomQuadtree.Add(MakeGlobalPoint((0.5 + x) * step, (0.5 + y) * step), 0.0 /* rank */);
|
||||
double const kCountPerTile = 1.0;
|
||||
std::map<int /* zoom */, size_t> populationCount;
|
||||
auto const incZoomPopulation = [&](double & rank, int zoom)
|
||||
{
|
||||
TEST_ALMOST_EQUAL_ABS(rank, 0.0, kEps, ());
|
||||
++populationCount[zoom];
|
||||
};
|
||||
minZoomQuadtree.SetMinZoom(kCountPerTile, scales::GetUpperStyleScale(), incZoomPopulation);
|
||||
TEST_EQUAL(populationCount.size(), kMaxDepth + 1, (populationCount));
|
||||
|
||||
auto p = populationCount.cbegin();
|
||||
ASSERT(p != populationCount.cend(), ());
|
||||
std::vector<size_t> partialSums;
|
||||
partialSums.push_back(p->second);
|
||||
while (++p != populationCount.cend())
|
||||
partialSums.push_back(partialSums.back() + p->second);
|
||||
TEST_EQUAL(partialSums.front(), 1, ());
|
||||
TEST_EQUAL(partialSums.back(), (1 << (kMaxDepth * 2)), ());
|
||||
auto const isGrowthExponential = [](size_t lhs, size_t rhs) { return rhs != 4 * lhs; };
|
||||
TEST(std::adjacent_find(partialSums.cbegin(), partialSums.cend(), isGrowthExponential) == partialSums.cend(),
|
||||
(partialSums));
|
||||
}
|
||||
|
||||
std::mt19937 g(0);
|
||||
|
||||
auto const gen = [&g] { return std::generate_canonical<double, std::numeric_limits<uint32_t>::digits>(g); };
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
kml::MinZoomQuadtree<double /* rank */, std::less<double>> minZoomQuadtree{{} /* less */};
|
||||
|
||||
size_t const kTotalCount = 1 + g() % 10000;
|
||||
for (size_t i = 0; i < kTotalCount; ++i)
|
||||
{
|
||||
auto const x = gen();
|
||||
auto const y = gen();
|
||||
auto const rank = gen();
|
||||
minZoomQuadtree.Add(MakeGlobalPoint(x, y), rank);
|
||||
}
|
||||
|
||||
double const kCountPerTile = 1.0 + kTotalCount * gen() / 2.0;
|
||||
std::map<int /* zoom */, size_t> populationCount;
|
||||
auto const incZoomPopulation = [&](double & /* rank */, int zoom) { ++populationCount[zoom]; };
|
||||
minZoomQuadtree.SetMinZoom(kCountPerTile, scales::GetUpperStyleScale(), incZoomPopulation);
|
||||
|
||||
auto p = populationCount.cbegin();
|
||||
ASSERT(p != populationCount.cend(), ());
|
||||
std::vector<size_t> partialSums;
|
||||
partialSums.push_back(p->second);
|
||||
while (++p != populationCount.cend())
|
||||
partialSums.push_back(partialSums.back() + p->second);
|
||||
TEST_EQUAL(partialSums.back(), kTotalCount, ());
|
||||
|
||||
double areaScale = 1.0;
|
||||
for (size_t i = 0; i < partialSums.size(); ++i)
|
||||
{
|
||||
auto const maxAbsErr = 4.0 * std::ceil(std::sqrt(kCountPerTile * areaScale)) + 4.0;
|
||||
TEST_LESS_OR_EQUAL(partialSums[i], kCountPerTile * areaScale + maxAbsErr,
|
||||
(kCountPerTile, maxAbsErr, partialSums));
|
||||
areaScale *= 4.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(Kml_MinzoomQuadtree_CornerCases)
|
||||
{
|
||||
{
|
||||
kml::MinZoomQuadtree<double /* rank */, std::less<double>> minZoomQuadtree{{} /* less */};
|
||||
minZoomQuadtree.Add(MakeGlobalPoint(0.5, 0.5), 0.0 /* rank */);
|
||||
double const kCountPerTile = 100.0;
|
||||
auto const checkRank = [&](double & rank, int zoom)
|
||||
{
|
||||
TEST_ALMOST_EQUAL_ABS(rank, 0.0, kEps, ());
|
||||
TEST_EQUAL(zoom, 1, ());
|
||||
};
|
||||
minZoomQuadtree.SetMinZoom(kCountPerTile, scales::GetUpperStyleScale(), checkRank);
|
||||
}
|
||||
|
||||
{
|
||||
kml::MinZoomQuadtree<double /* rank */, std::less<double>> minZoomQuadtree{{} /* less */};
|
||||
double const kCountPerTile = 100.0;
|
||||
auto const unreachable = [&](double & /* rank */, int /* zoom */) { TEST(false, ()); };
|
||||
minZoomQuadtree.SetMinZoom(kCountPerTile, scales::GetUpperStyleScale(), unreachable);
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(Kml_MinzoomQuadtree_MaxZoom)
|
||||
{
|
||||
kml::MinZoomQuadtree<double /* rank */, std::less<double>> minZoomQuadtree{{} /* less */};
|
||||
size_t const kMaxDepth = 5;
|
||||
double const step = 1.0 / (1 << kMaxDepth);
|
||||
for (int x = 0; x < (1 << kMaxDepth); ++x)
|
||||
for (int y = 0; y < (1 << kMaxDepth); ++y)
|
||||
minZoomQuadtree.Add(MakeGlobalPoint((0.5 + x) * step, (0.5 + y) * step), 0.0 /* rank */);
|
||||
double const kCountPerTile = 1.0;
|
||||
std::map<int /* zoom */, size_t> populationCount;
|
||||
auto const incZoomPopulation = [&](double & rank, int zoom)
|
||||
{
|
||||
TEST_ALMOST_EQUAL_ABS(rank, 0.0, kEps, ());
|
||||
++populationCount[zoom];
|
||||
};
|
||||
int const kMaxZoom = 4;
|
||||
minZoomQuadtree.SetMinZoom(kCountPerTile, kMaxZoom, incZoomPopulation);
|
||||
TEST_EQUAL(populationCount.size(), kMaxZoom, (populationCount));
|
||||
|
||||
auto p = populationCount.cbegin();
|
||||
ASSERT(p != populationCount.cend(), ());
|
||||
std::vector<size_t> partialSums;
|
||||
partialSums.push_back(p->second);
|
||||
while (++p != populationCount.cend())
|
||||
partialSums.push_back(partialSums.back() + p->second);
|
||||
TEST_EQUAL(partialSums.front(), 1, ());
|
||||
TEST_EQUAL(partialSums.back(), (1 << (kMaxDepth * 2)), ());
|
||||
auto const isGrowthExponential = [](size_t lhs, size_t rhs) { return rhs != 4 * lhs; };
|
||||
TEST(std::adjacent_find(partialSums.cbegin(), std::prev(partialSums.cend()), isGrowthExponential) ==
|
||||
std::prev(partialSums.cend()),
|
||||
(partialSums));
|
||||
TEST_LESS_OR_EQUAL(4 * *std::prev(partialSums.cend(), 2), partialSums.back(), ());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue