diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 55d87d988..e76367404 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -11,8 +11,6 @@ #include #include #include -#include -#include #include #include #include @@ -294,47 +292,6 @@ TileData::State Source::addTile(const TileID& tileID, const StyleUpdateParameter return newState; } -double Source::getZoom(const TransformState& state) const { - double offset = std::log(util::tileSize / tileSize) / std::log(2); - return state.getZoom() + offset; -} - -int32_t Source::coveringZoomLevel(const TransformState& state) const { - double zoom = getZoom(state); - if (type == SourceType::Raster || type == SourceType::Video) { - zoom = ::round(zoom); - } else { - zoom = std::floor(zoom); - } - return util::clamp(zoom, state.getMinZoom(), state.getMaxZoom()); -} - -std::forward_list Source::coveringTiles(const TransformState& state) const { - int32_t z = coveringZoomLevel(state); - - auto actualZ = z; - const bool reparseOverscaled = - type == SourceType::Vector || - type == SourceType::Annotations; - - if (z < info->minZoom) return {{}}; - if (z > info->maxZoom) z = info->maxZoom; - - // Map four viewport corners to pixel coordinates - box points = state.cornersToBox(z); - const TileCoordinate center = state.pointToCoordinate({ state.getWidth() / 2.0f, state.getHeight()/ 2.0f }).zoomTo(z); - - std::forward_list covering_tiles = tileCover(z, points, reparseOverscaled ? actualZ : z); - - covering_tiles.sort([¢er](const TileID& a, const TileID& b) { - // Sorts by distance from the box center - return std::fabs(a.x - center.column) + std::fabs(a.y - center.row) < - std::fabs(b.x - center.column) + std::fabs(b.y - center.row); - }); - - return covering_tiles; -} - /** * Recursively find children of the given tile that are already loaded. * @@ -344,14 +301,14 @@ std::forward_list Source::coveringTiles(const TransformState& state) con * * @return boolean Whether the children found completely cover the tile. */ -bool Source::findLoadedChildren(const TileID& tileID, int32_t maxCoveringZoom, std::forward_list& retain) { +bool Source::findLoadedChildren(const TileID& tileID, int32_t maxCoveringZoom, std::vector& retain) { bool complete = true; int32_t z = tileID.z; auto ids = tileID.children(info->maxZoom); for (const auto& child_id : ids) { const TileData::State state = hasTile(child_id); if (TileData::isReadyState(state)) { - retain.emplace_front(child_id); + retain.emplace_back(child_id); } if (state != TileData::State::parsed) { complete = false; @@ -373,12 +330,12 @@ bool Source::findLoadedChildren(const TileID& tileID, int32_t maxCoveringZoom, s * * @return boolean Whether a parent was found. */ -void Source::findLoadedParent(const TileID& tileID, int32_t minCoveringZoom, std::forward_list& retain) { +void Source::findLoadedParent(const TileID& tileID, int32_t minCoveringZoom, std::vector& retain) { for (int32_t z = tileID.z - 1; z >= minCoveringZoom; --z) { const TileID parent_id = tileID.parent(z, info->maxZoom); const TileData::State state = hasTile(parent_id); if (TileData::isReadyState(state)) { - retain.emplace_front(parent_id); + retain.emplace_back(parent_id); if (state == TileData::State::parsed) { return; } @@ -393,17 +350,29 @@ bool Source::update(const StyleUpdateParameters& parameters) { return allTilesUpdated; } - double zoom = coveringZoomLevel(parameters.transformState); - std::forward_list required = coveringTiles(parameters.transformState); - - // Determine the overzooming/underzooming amounts. + // Determine the overzooming/underzooming amounts and required tiles. + std::vector required; + int32_t zoom = coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize); int32_t minCoveringZoom = util::clamp(zoom - 10, info->minZoom, info->maxZoom); int32_t maxCoveringZoom = util::clamp(zoom + 1, info->minZoom, info->maxZoom); + if (zoom >= info->minZoom) { + const bool reparseOverscaled = + type == SourceType::Vector || + type == SourceType::Annotations; + + const auto actualZ = zoom; + if (zoom > info->maxZoom) { + zoom = info->maxZoom; + } + + required = tileCover(parameters.transformState, zoom, reparseOverscaled ? actualZ : zoom); + } + // Retain is a list of tiles that we shouldn't delete, even if they are not // the most ideal tile for the current viewport. This may include tiles like // parent or child tiles that are *already* loaded. - std::forward_list retain(required); + std::vector retain(required); // Add existing child/parent tiles if the actual tile is not yet loaded for (const auto& tileID : required) { diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index cca160a72..7f69fc288 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include namespace mapbox { @@ -82,17 +82,13 @@ private: std::exception_ptr, bool isNewTile); bool handlePartialTile(const TileID&); - bool findLoadedChildren(const TileID&, int32_t maxCoveringZoom, std::forward_list& retain); - void findLoadedParent(const TileID&, int32_t minCoveringZoom, std::forward_list& retain); - int32_t coveringZoomLevel(const TransformState&) const; - std::forward_list coveringTiles(const TransformState&) const; + bool findLoadedChildren(const TileID&, int32_t maxCoveringZoom, std::vector& retain); + void findLoadedParent(const TileID&, int32_t minCoveringZoom, std::vector& retain); TileData::State addTile(const TileID&, const StyleUpdateParameters&); TileData::State hasTile(const TileID&); void updateTilePtrs(); - double getZoom(const TransformState &state) const; - private: std::unique_ptr info; diff --git a/src/mbgl/map/tile_id.hpp b/src/mbgl/map/tile_id.hpp index 3ecc5c6ce..a193b6339 100644 --- a/src/mbgl/map/tile_id.hpp +++ b/src/mbgl/map/tile_id.hpp @@ -26,12 +26,6 @@ public: return ((std::pow(2, z) * y + x) * 32) + z; } - struct Hash { - std::size_t operator()(const TileID& id) const { - return std::hash()(id.to_uint64()); - } - }; - inline bool operator==(const TileID& rhs) const { return w == rhs.w && z == rhs.z && x == rhs.x && y == rhs.y; } @@ -53,9 +47,20 @@ public: children(int8_t sourceMaxZoom = std::numeric_limits::max()) const; bool isChildOf(const TileID&) const; operator std::string() const; - }; } // namespace mbgl +namespace std { +template <> +struct hash { + typedef mbgl::TileID argument_type; + typedef std::size_t result_type; + + result_type operator()(const mbgl::TileID& id) const { + return std::hash()(id.to_uint64()); + } +}; +} // namespace std + #endif diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index 5e74f7622..42fbd9af8 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -54,18 +53,6 @@ void TransformState::getProjMatrix(mat4& projMatrix) const { pixel_y() - getHeight() / 2.0f, 0); } -box TransformState::cornersToBox(uint32_t z) const { - double w = width; - double h = height; - box b( - pointToCoordinate({ 0, 0 }).zoomTo(z), - pointToCoordinate({ w, 0 }).zoomTo(z), - pointToCoordinate({ w, h }).zoomTo(z), - pointToCoordinate({ 0, h }).zoomTo(z)); - return b; -} - - #pragma mark - Dimensions uint16_t TransformState::getWidth() const { diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 2aef5707f..a276c0886 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -15,8 +15,7 @@ namespace mbgl { class TileID; -struct box; -struct TileCoordinate; +class TileCoordinate; class TransformState { friend class Transform; @@ -27,7 +26,6 @@ public: // Matrix void matrixFor(mat4& matrix, const TileID& id, const int8_t z) const; void getProjMatrix(mat4& matrix) const; - box cornersToBox(uint32_t z) const; // Dimensions uint16_t getWidth() const; diff --git a/src/mbgl/util/box.hpp b/src/mbgl/util/box.hpp deleted file mode 100644 index 2806fb95f..000000000 --- a/src/mbgl/util/box.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef MBGL_UTIL_BOX -#define MBGL_UTIL_BOX - -#include - -namespace mbgl { - -struct box { - box(TileCoordinate tl_, TileCoordinate tr_, TileCoordinate br_, TileCoordinate bl_) : - tl(tl_), tr(tr_), br(br_), bl(bl_) {} - TileCoordinate tl, tr, br, bl; -}; - -} // namespace mbgl - -#endif diff --git a/src/mbgl/util/tile_coordinate.hpp b/src/mbgl/util/tile_coordinate.hpp index 4710e08d0..a989bef0c 100644 --- a/src/mbgl/util/tile_coordinate.hpp +++ b/src/mbgl/util/tile_coordinate.hpp @@ -5,23 +5,11 @@ namespace mbgl { -struct TileCoordinate { +class TileCoordinate { +public: double column; double row; double zoom; - - TileCoordinate(double column_, double row_, double zoom_) : - column(column_), row(row_), zoom(zoom_) {} - - TileCoordinate zoomTo(double targetZoom) { - double scale = std::pow(2, targetZoom - zoom); - return { column * scale, row * scale, targetZoom }; - } - - TileCoordinate operator-(TileCoordinate c) { - c = c.zoomTo(zoom); - return { column - c.column, row - c.row, zoom }; - }; }; } // namespace mbgl diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index 803aa53ee..a9c265089 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -1,7 +1,9 @@ #include #include -#include #include +#include +#include +#include namespace mbgl { @@ -66,7 +68,27 @@ static void scanTriangle(const mbgl::vec2 a, const mbgl::vec2 b, if (bc.dy) scanSpans(ca, bc, ymin, ymax, scanLine); } -std::forward_list tileCover(int8_t z, const mbgl::box &bounds, int8_t actualZ) { +int32_t coveringZoomLevel(double zoom, SourceType type, uint16_t tileSize) { + zoom += std::log(util::tileSize / tileSize) / std::log(2); + if (type == SourceType::Raster || type == SourceType::Video) { + return ::round(zoom); + } else { + return std::floor(zoom); + } +} + +static mbgl::vec2 zoomTo(const TileCoordinate& c, double z) { + double scale = std::pow(2, z - c.zoom); + return { c.column * scale, c.row * scale }; +} + +std::vector tileCover(const TileCoordinate& tl_, + const TileCoordinate& tr_, + const TileCoordinate& br_, + const TileCoordinate& bl_, + const TileCoordinate& center, + int32_t z, + int32_t actualZ) { int32_t tiles = 1 << z; std::forward_list t; @@ -79,10 +101,11 @@ std::forward_list tileCover(int8_t z, const mbgl::box &bounds, int8_t ac } }; - mbgl::vec2 tl = { bounds.tl.column, bounds.tl.row }; - mbgl::vec2 tr = { bounds.tr.column, bounds.tr.row }; - mbgl::vec2 br = { bounds.br.column, bounds.br.row }; - mbgl::vec2 bl = { bounds.bl.column, bounds.bl.row }; + mbgl::vec2 tl = zoomTo(tl_, z); + mbgl::vec2 tr = zoomTo(tr_, z); + mbgl::vec2 br = zoomTo(br_, z); + mbgl::vec2 bl = zoomTo(bl_, z); + mbgl::vec2 c = zoomTo(center, z); // Divide the screen up in two triangles and scan each of them: // \---+ @@ -94,7 +117,46 @@ std::forward_list tileCover(int8_t z, const mbgl::box &bounds, int8_t ac t.sort(); t.unique(); - return t; + t.sort([&](const TileID& a, const TileID& b) { + // Sorts by distance from the box center + return std::fabs(a.x - c.x) + std::fabs(a.y - c.y) < + std::fabs(b.x - c.x) + std::fabs(b.y - c.y); + }); + + return std::vector(t.begin(), t.end()); +} + +std::vector tileCover(const LatLngBounds& bounds_, int32_t z, int32_t actualZ) { + if (bounds_.isEmpty() || + bounds_.south() > util::LATITUDE_MAX || + bounds_.north() < -util::LATITUDE_MAX) { + return {}; + } + + LatLngBounds bounds = LatLngBounds::hull( + { std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() }, + { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() }); + + const TransformState state; + return tileCover( + state.latLngToCoordinate(bounds.northwest()), + state.latLngToCoordinate(bounds.northeast()), + state.latLngToCoordinate(bounds.southeast()), + state.latLngToCoordinate(bounds.southwest()), + state.latLngToCoordinate(bounds.center()), + z, actualZ); +} + +std::vector tileCover(const TransformState& state, int32_t z, int32_t actualZ) { + const double w = state.getWidth(); + const double h = state.getHeight(); + return tileCover( + state.pointToCoordinate({ 0, 0 }), + state.pointToCoordinate({ w, 0 }), + state.pointToCoordinate({ w, h }), + state.pointToCoordinate({ 0, h }), + state.pointToCoordinate({ w/2, h/2 }), + z, actualZ); } } // namespace mbgl diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp index 0514c36b6..a489964ab 100644 --- a/src/mbgl/util/tile_cover.hpp +++ b/src/mbgl/util/tile_cover.hpp @@ -2,13 +2,19 @@ #define MBGL_UTIL_TILE_COVER #include -#include +#include -#include +#include namespace mbgl { -std::forward_list tileCover(int8_t z, const box& bounds, int8_t actualZ); +class TransformState; +class LatLngBounds; + +int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize); + +std::vector tileCover(const TransformState&, int32_t z, int32_t actualZ); +std::vector tileCover(const LatLngBounds&, int32_t z, int32_t actualZ); } // namespace mbgl diff --git a/test/test.gypi b/test/test.gypi index 34c9d8dd5..22e3d0184 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -47,6 +47,7 @@ 'util/text_conversions.cpp', 'util/thread.cpp', 'util/thread_local.cpp', + 'util/tile_cover.cpp', 'util/timer.cpp', 'util/token.cpp', 'util/work_queue.cpp', diff --git a/test/util/tile_cover.cpp b/test/util/tile_cover.cpp new file mode 100644 index 000000000..d003653fd --- /dev/null +++ b/test/util/tile_cover.cpp @@ -0,0 +1,118 @@ +#include +#include +#include + +#include + +#include + +using namespace mbgl; +using set = std::unordered_set; + +TEST(TileCover, Empty) { + auto result = tileCover(LatLngBounds::empty(), 0, 0); + ASSERT_TRUE(result.empty()); +} + +TEST(TileCover, Arctic) { + auto result = tileCover(LatLngBounds::hull({ 86, -180 }, { 90, 180 }), 0, 0); + ASSERT_TRUE(result.empty()); +} + +TEST(TileCover, Antarctic) { + auto result = tileCover(LatLngBounds::hull({ -86, -180 }, { -90, 180 }), 0, 0); + ASSERT_TRUE(result.empty()); +} + +TEST(TileCover, WorldZ0) { + auto result = tileCover(LatLngBounds::world(), 0, 0); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(0, result[0].z); + ASSERT_EQ(0, result[0].x); + ASSERT_EQ(0, result[0].y); +} + +TEST(TileCover, WorldZ1) { + auto result = tileCover(LatLngBounds::world(), 1, 1); + ASSERT_EQ(4, result.size()); + ASSERT_EQ( + (set {{ + TileID(1, 1, 1, 1), + TileID(1, 0, 1, 1), + TileID(1, 1, 0, 1), + TileID(1, 0, 0, 1) + }}), + (set { + result.begin(), + result.end() + })); +} + +//TEST(TileCover, SingletonZ0) { +// auto result = tileCover(LatLngBounds::singleton({0, 0}), 0, 0); +// ASSERT_EQ(1, result.size()); +// ASSERT_EQ(0, result[0].z); +// ASSERT_EQ(0, result[0].x); +// ASSERT_EQ(0, result[0].y); +//} +// +//TEST(TileCover, SingletonZ1) { +// auto result = tileCover(LatLngBounds::singleton({0, 0}), 1, 1); +// ASSERT_EQ(1, result.size()); +// ASSERT_EQ(0, result[0].z); +// ASSERT_EQ(0, result[0].x); +// ASSERT_EQ(0, result[0].y); +//} + +static const LatLngBounds sanFrancisco = LatLngBounds::hull( + { 37.6609, -122.5744 }, + { 37.8271, -122.3204 }); + +TEST(TileCover, SanFranciscoZ0) { + auto result = tileCover(sanFrancisco, 0, 0); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(0, result[0].w); + ASSERT_EQ(0, result[0].z); + ASSERT_EQ(0, result[0].x); + ASSERT_EQ(0, result[0].y); +} + +TEST(TileCover, SanFranciscoZ10) { + auto result = tileCover(sanFrancisco, 10, 10); + ASSERT_EQ(4, result.size()); + ASSERT_EQ( + (set {{ + TileID(10, 163, 395, 10), + TileID(10, 164, 395, 10), + TileID(10, 163, 396, 10), + TileID(10, 164, 396, 10) + }}), + (set { + result.begin(), + result.end() + })); +} + +//TEST(TileCover, OrderedByDistanceToCenter) { +// auto result = tileCover(sanFrancisco, 12, 12); +// ASSERT_EQ(12, result.size()); +// ASSERT_EQ( 12, result[0].z); +// ASSERT_EQ( 654, result[0].x); +// ASSERT_EQ(1583, result[0].y); +// ASSERT_EQ( 12, result[1].z); +// ASSERT_EQ( 655, result[1].x); +// ASSERT_EQ(1583, result[1].y); +//} +// +//static const LatLngBounds sanFranciscoWrapped = LatLngBounds::hull( +// { 37.6609, 238.5744 }, +// { 37.8271, 238.3204 }); +// +//TEST(TileCover, SanFranciscoZ0Wrapped) { +// auto result = tileCover(sanFranciscoWrapped, 0, 0); +// ASSERT_EQ(1, result.size()); +// ASSERT_EQ(1, result[0].w); +// ASSERT_EQ(0, result[0].z); +// ASSERT_EQ(0, result[0].x); +// ASSERT_EQ(0, result[0].y); +//}