#include #include #include #include #include #include /* srand, rand */ #include /* time */ #include using namespace mbgl; TEST(TileCover, Empty) { EXPECT_EQ((std::vector{}), util::tileCover(LatLngBounds::empty(), 0)); } TEST(TileCover, Arctic) { EXPECT_EQ((std::vector{}), util::tileCover(LatLngBounds::hull({86, -180}, {90, 180}), 0)); } TEST(TileCover, Antarctic) { EXPECT_EQ((std::vector{}), util::tileCover(LatLngBounds::hull({-86, -180}, {-90, 180}), 0)); } TEST(TileCover, WorldZ0) { EXPECT_EQ((std::vector{ {0, 0, 0}, }), util::tileCover(LatLngBounds::world(), 0)); } TEST(TileCover, Pitch) { Transform transform; transform.resize({512, 512}); // slightly offset center so that tile order is better defined transform.jumpTo(CameraOptions() .withCenter(LatLng{ 0.1, -0.1, }) .withZoom(2.0) .withBearing(5.0) .withPitch(40.0)); EXPECT_EQ((std::vector{{2, 1, 1}, {2, 2, 1}, {2, 1, 2}, {2, 2, 2}}), util::tileCover({transform.getState()}, 2)); } TEST(TileCover, PitchIssue15442) { Transform transform; transform.resize({412, 691}); transform.jumpTo(CameraOptions() .withCenter(LatLng{ 59.116898740996106, 91.565660781803615, }) .withZoom(2.0551126748417214) .withBearing(util::rad2deg(0.74963938256567264)) .withPitch(util::rad2deg(1.0471975511965976))); EXPECT_EQ((std::vector{ {2, 3, 1}, {2, 2, 1}, {2, 3, 0}, {2, 2, 0}, {2, 1, {2, 0, 0}}, {2, 1, {2, 1, 0}}}), util::tileCover({transform.getState()}, 2)); } TEST(TileCover, PitchOverAllowedByContentInsets) { Transform transform; transform.resize({512, 512}); transform.jumpTo(CameraOptions() .withCenter(LatLng{0.1, -0.1}) .withPadding(EdgeInsets{376, 0, 0, 0}) .withZoom(8.0) .withBearing(45.0) .withPitch(60.0)); // Top padding of 376 leads to capped pitch. See Transform::getMaxPitchForEdgeInsets. EXPECT_LE(transform.getPitch() + 0.001, util::deg2rad(60)); EXPECT_EQ((std::vector{{3, 4, 3}, {3, 3, 3}, {3, 4, 4}, {3, 3, 4}}), util::tileCover({transform.getState()}, 3)); } TEST(TileCover, PitchWithLargerResultSet) { Transform transform; transform.resize({1024, 768}); // The values used here triggered the regression with left and right edge // selection in tile_cover.cpp scanSpans. transform.jumpTo(CameraOptions() .withCenter(LatLng{0.1, -0.1}) .withPadding(EdgeInsets{400, 0, 0, 0}) .withZoom(5) .withBearing(-142.2630000003529176) .withPitch(60.0)); auto cover = util::tileCover({.transformState = transform.getState()}, 5); // Returned vector has above 100 elements, we check first 16 as there is a // plan to return lower LOD for distant tiles. EXPECT_EQ((std::vector{{5, 15, 16}, {5, 15, 17}, {5, 14, 16}, {5, 16, 16}, {5, 15, 15}, {5, 14, 17}, {5, 16, 17}, {5, 14, 15}, {5, 16, 15}, {5, 15, 18}, {5, 13, 16}, {5, 14, 18}, {5, 16, 18}, {5, 13, 17}, {5, 13, 18}, {5, 15, 19}}), (std::vector{cover.begin(), cover.begin() + 16})); } TEST(TileCover, WorldZ1) { EXPECT_EQ((std::vector{ {1, 0, 0}, {1, 0, 1}, {1, 1, 0}, {1, 1, 1}, }), util::tileCover(LatLngBounds::world(), 1)); } TEST(TileCover, SingletonZ0) { EXPECT_EQ((std::vector{}), util::tileCover(LatLngBounds::singleton({0, 0}), 0)); } TEST(TileCover, SingletonZ1) { EXPECT_EQ((std::vector{}), util::tileCover(LatLngBounds::singleton({0, 0}), 1)); } TEST(TileCover, CoordinatesAreUnwrapped) { Transform transform; transform.resize({512, 512}); transform.jumpTo(CameraOptions() .withCenter(LatLng{ 0.1, -180.1, }) .withZoom(1.0)); EXPECT_EQ( (std::vector{{1, 0, {1, 1, 0}}, {1, 1, {1, 0, 0}}, {1, 0, {1, 1, 1}}, {1, 1, {1, 0, 1}}}), util::tileCover({transform.getState()}, 1)); } TEST(TileCover, DifferentOverscaledZ) { Transform transform; transform.resize({512, 512}); // slightly offset center so that tile order is better defined transform.jumpTo(CameraOptions() .withCenter(LatLng{ 0.1, -0.1, }) .withZoom(2.0)); EXPECT_EQ( (std::vector{{3, 0, {2, 1, 1}}, {3, 0, {2, 2, 1}}, {3, 0, {2, 1, 2}}, {3, 0, {2, 2, 2}}}), util::tileCover({transform.getState()}, 2, 3)); } TEST(TileCover, DifferentOverscaledZWithPitch) { Transform transform; transform.resize({512, 512}); transform.jumpTo(CameraOptions() .withCenter(LatLng{ 30.0, -10.0, }) .withZoom(3.5) .withPitch(60)); EXPECT_EQ((std::vector{{5, 0, {3, 3, 3}}, {5, 0, {3, 4, 3}}, {5, 0, {3, 3, 2}}, {5, 0, {3, 4, 2}}, {5, 0, {3, 3, 1}}, {5, 0, {3, 4, 1}}, {5, 0, {3, 2, 1}}}), util::tileCover({transform.getState()}, 3, 5)); } TEST(TileCover, DifferentOverscaledZWrapped) { Transform transform; transform.resize({512, 512}); transform.jumpTo(CameraOptions() .withCenter(LatLng{ 0.1, -180.1, }) .withZoom(1.0)); EXPECT_EQ( (std::vector{{2, 0, {1, 1, 0}}, {2, 1, {1, 0, 0}}, {2, 0, {1, 1, 1}}, {2, 1, {1, 0, 1}}}), util::tileCover({transform.getState()}, 1, 2)); } TEST(TileCover, FlippedY) { Transform transform; transform.resize({512, 512}); transform.setViewportMode(ViewportMode::FlippedY); transform.jumpTo(CameraOptions() .withCenter(LatLng{ 0.2, -0.1, }) .withZoom(1.0)); EXPECT_EQ((std::vector{{1, 0, 0}, {1, 1, 0}, {1, 0, 1}, {1, 1, 1}}), util::tileCover({transform.getState()}, 1)); } TEST(TileCover, FlippedYPitch) { Transform transform; transform.resize({512, 512}); transform.setViewportMode(ViewportMode::FlippedY); transform.jumpTo(CameraOptions() .withCenter(LatLng{ 0.1, -0.1, }) .withZoom(2.0) .withBearing(5.0) .withPitch(40.0)); EXPECT_EQ((std::vector{{2, 1, 1}, {2, 2, 1}, {2, 1, 2}, {2, 2, 2}}), util::tileCover({transform.getState()}, 2)); } TEST(TileCover, FlippedYHelsinki) { Transform transform; transform.resize({512, 512}); transform.setViewportMode(ViewportMode::FlippedY); transform.jumpTo(CameraOptions() .withCenter(LatLng{ 60.167231, 24.942063, }) .withZoom(11.447425)); EXPECT_EQ((std::vector{{11, 1165, 592}, {11, 1166, 592}, {11, 1165, 593}, {11, 1166, 593}}), util::tileCover({transform.getState()}, 11)); } TEST(TileCoverStream, Arctic) { auto bounds = LatLngBounds::hull({84, -180}, {70, 180}); auto zoom = 3; util::TileCover tc(bounds, zoom); auto results = util::tileCover(bounds, zoom); std::vector t; while (tc.hasNext()) { t.push_back(*tc.next()); }; EXPECT_EQ(t.size(), results.size()); } TEST(TileCoverStream, WorldZ1) { auto zoom = 1; util::TileCover tc(LatLngBounds::world(), zoom); std::vector t; while (tc.hasNext()) { t.push_back(*tc.next()); }; EXPECT_EQ((std::vector{ {1, 0, 0}, {1, 1, 0}, {1, 0, 1}, {1, 1, 1}, }), t); } static const LatLngBounds sanFrancisco = LatLngBounds::hull({37.6609, -122.5744}, {37.8271, -122.3204}); TEST(TileCover, SanFranciscoZ0) { EXPECT_EQ((std::vector{ {0, 0, 0}, }), util::tileCover(sanFrancisco, 0)); } TEST(TileCover, SanFranciscoZ10) { EXPECT_EQ((std::vector{ {10, 163, 395}, {10, 163, 396}, {10, 164, 395}, {10, 164, 396}, }), util::tileCover(sanFrancisco, 10)); } static const LatLngBounds sanFranciscoWrapped = LatLngBounds::hull({37.6609, 238.5744}, {37.8271, 238.3204}); TEST(TileCover, SanFranciscoZ0Wrapped) { EXPECT_EQ((std::vector{{0, 1, 0}}), util::tileCover(sanFranciscoWrapped, 0)); } TEST(TileCover, GeomPoint) { auto point = Point{-122.5744, 37.6609}; EXPECT_EQ((std::vector{{2, 0, 1}}), util::tileCover(point, 2)); } TEST(TileCover, GeomMultiPoint) { auto points = MultiPoint{{-122.5, 37.76}, {-122.4, 37.76}}; EXPECT_EQ((std::vector{{20, 167480, 405351}, {20, 167772, 405351}}), util::tileCover(points, 20)); } TEST(TileCover, GeomLineZ10) { auto lineCover = util::tileCover( LineString{{-121.49368286132812, 38.57903714667459}, {-122.4422836303711, 37.773157169570695}}, 10); EXPECT_EQ((std::vector{{10, 166, 392}, {10, 165, 393}, {10, 166, 393}, {10, 164, 394}, {10, 165, 394}, {10, 163, 395}, {10, 164, 395}}), lineCover); } TEST(TileCover, GeomLineRegression11870) { auto lineCover = util::tileCover( LineString{ {-121.5063900000001, 40.470099999999945}, {-121.5065300000001, 40.470369999999946}, {-121.5065900000001, 40.470519999999944}, }, 14); EXPECT_EQ((std::vector{{14, 2662, 6174}}), lineCover); } TEST(TileCover, WrappedGeomLineZ10) { auto lineCover = util::tileCover( LineString{{-179.93342914581299, 38.892101707724315}, {-180.02394485473633, 38.89203490311832}}, 10); EXPECT_EQ((std::vector{{10, -1, 391}, {10, 0, 391}}), lineCover); lineCover = util::tileCover( LineString{{179.93342914581299, 38.892101707724315}, {180.02394485473633, 38.89203490311832}}, 10); EXPECT_EQ((std::vector{{10, 1023, 391}, {10, 1024, 391}}), lineCover); } TEST(TileCover, GeomMultiLineString) { auto geom = MultiLineString{{{-122.5, 37.76}, {-122.4, 37.76}}, {{-122.5, 37.72}, {-122.4, 37.72}}}; EXPECT_EQ((std::vector{{14, 2616, 6333}, {14, 2617, 6333}, {14, 2618, 6333}, {14, 2619, 6333}, {14, 2620, 6333}, {14, 2621, 6333}, {14, 2616, 6335}, {14, 2617, 6335}, {14, 2618, 6335}, {14, 2619, 6335}, {14, 2620, 6335}, {14, 2621, 6335}}), util::tileCover(geom, 14)); } TEST(TileCover, GeomPolygon) { auto polygon = Polygon{{ {5.09765625, 53.067626642387374}, {2.373046875, 43.389081939117496}, {-4.74609375, 48.45835188280866}, {-1.494140625, 37.09023980307208}, {22.587890625, 36.24427318493909}, {31.640625, 46.13417004624326}, {17.841796875, 54.7246201949245}, {5.09765625, 53.067626642387374}, }, {{19.6875, 49.66762782262194}, {22.8515625, 43.51668853502906}, {13.623046875, 45.089035564831036}, {16.34765625, 39.095962936305476}, {5.185546875, 41.244772343082076}, {8.701171874999998, 50.233151832472245}, {19.6875, 49.66762782262194}}}; auto results = util::tileCover(polygon, 8); EXPECT_NE(std::ranges::find(results, UnwrappedTileID{8, 134, 87}), results.end()); EXPECT_NE(std::ranges::find(results, UnwrappedTileID{8, 139, 87}), results.end()); // Should have a hole EXPECT_EQ(std::ranges::find(results, UnwrappedTileID{8, 136, 87}), results.end()); } TEST(TileCover, GeomMultiPolygon) { auto multiPolygon = MultiPolygon{{{ {5.09765625, 53.067626642387374}, {2.373046875, 43.389081939117496}, {-4.74609375, 48.45835188280866}, {-1.494140625, 37.09023980307208}, {22.587890625, 36.24427318493909}, {31.640625, 46.13417004624326}, {17.841796875, 54.7246201949245}, {5.09765625, 53.067626642387374}, }}, {{{59.150390625, 45.460130637921004}, {65.126953125, 41.11246878918088}, {69.169921875, 47.45780853075031}, {63.896484375, 50.064191736659104}, {59.150390625, 45.460130637921004}}}}; auto results = util::tileCover(multiPolygon, 8); EXPECT_EQ(424u, results.size()); EXPECT_NE(std::ranges::find(results, UnwrappedTileID{8, 139, 87}), results.end()); EXPECT_NE(std::ranges::find(results, UnwrappedTileID{8, 136, 87}), results.end()); EXPECT_NE(std::ranges::find(results, UnwrappedTileID{8, 174, 94}), results.end()); } TEST(TileCover, GeomSanFranciscoPoly) { auto sanFranciscoGeom = Polygon{ {{-122.5143814086914, 37.779127216982424}, {-122.50811576843262, 37.72721239056709}, {-122.50313758850099, 37.70820178063929}, {-122.3938751220703, 37.707454835665274}, {-122.37567901611328, 37.70663997801684}, {-122.36297607421874, 37.71343018466285}, {-122.354736328125, 37.727280276860036}, {-122.36469268798828, 37.73868429065797}, {-122.38014221191408, 37.75442980295571}, {-122.38391876220702, 37.78753873820529}, {-122.35919952392578, 37.8065289741725}, {-122.35679626464844, 37.820632846207864}, {-122.3712158203125, 37.835276322922695}, {-122.3818588256836, 37.82958198283902}, {-122.37190246582031, 37.80788523279169}, {-122.38735198974608, 37.791337175930686}, {-122.40966796874999, 37.812767557570204}, {-122.46425628662108, 37.807071480609274}, {-122.46803283691405, 37.810326435534755}, {-122.47901916503906, 37.81168262440736}, {-122.48966217041016, 37.78916666399649}, {-122.50579833984375, 37.78781006166096}, {-122.5143814086914, 37.779127216982424}}}; EXPECT_EQ( (std::vector{ {12, 654, 1582}, {12, 655, 1582}, {12, 654, 1583}, {12, 655, 1583}, {12, 654, 1584}, {12, 655, 1584}}), util::tileCover(sanFranciscoGeom, 12)); } TEST(TileCover, GeomInvalid) { auto point = Point{-122.5744, 97.6609}; EXPECT_THROW(util::tileCover(point, 2), std::domain_error); auto badLine = LineString{{1.0, 35.0}}; EXPECT_EQ((std::vector{}), util::tileCover(badLine, 16)); auto badPoly = Polygon{{{1.0, 35.0}}}; EXPECT_EQ((std::vector{}), util::tileCover(badPoly, 16)); // Should handle open polygons. badPoly = Polygon{{{1.0, 34.2}, {1.0, 34.4}, {0.5, 34.3}}}; EXPECT_EQ((std::vector{{10, 513, 407}, {10, 514, 407}, {10, 513, 408}, {10, 514, 408}}), util::tileCover(badPoly, 10)); } TEST(TileCount, World) { EXPECT_EQ(1u, util::tileCount(LatLngBounds::world(), 0)); EXPECT_EQ(4u, util::tileCount(LatLngBounds::world(), 1)); } TEST(TileCount, SanFranciscoZ10) { EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10)); } TEST(TileCount, SanFranciscoWrappedZ10) { EXPECT_EQ(4u, util::tileCount(sanFranciscoWrapped, 10)); } TEST(TileCount, SanFranciscoZ22) { EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22)); } TEST(TileCount, BoundsCrossingAntimeridian) { auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); EXPECT_EQ(1u, util::tileCount(crossingBounds, 0)); EXPECT_EQ(4u, util::tileCount(crossingBounds, 3)); EXPECT_EQ(8u, util::tileCount(crossingBounds, 4)); } TEST(TileCover, DISABLED_FuzzPoly) { while (true) { std::srand(static_cast(time(nullptr))); std::size_t len = std::rand() % 10000 + 3; Polygon polygon; std::size_t num_rings = 1; num_rings += std::rand() % 5; while (num_rings > 0) { LinearRing ring; for (std::size_t i = 0; i < len; ++i) { double x = std::rand() % 180; double y = std::rand() % 90; ring.push_back({x, y}); } polygon.emplace_back(ring); --num_rings; } std::clog << "."; util::TileCover tc(polygon, 5); while (tc.next()) { }; } } TEST(TileCover, DISABLED_FuzzLine) { while (true) { std::srand(static_cast(time(nullptr))); std::size_t len = std::rand() % 10000 + 3; MultiLineString mls; std::size_t num_lines = 1; num_lines += std::rand() % 5; while (num_lines > 0) { LineString line; for (std::size_t i = 0; i < len; ++i) { double x = std::rand() % 180; double y = std::rand() % 90; line.push_back({x, y}); } mls.emplace_back(line); --num_lines; } std::clog << "."; util::TileCover tc(mls, 5); while (tc.next()) { }; } }