[core] retain tiles differently and remove old TileID class

This commit is contained in:
Konstantin Käfer
2016-05-10 11:48:22 +02:00
parent ec70125e41
commit 7332ae0073
25 changed files with 859 additions and 675 deletions

View File

@@ -30,7 +30,7 @@ public:
OfflineTilePyramidRegionDefinition(const std::string&, const LatLngBounds&, double, double, float);
/* Private */
std::vector<TileID> tileCover(SourceType, uint16_t tileSize, const SourceInfo&) const;
std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const SourceInfo&) const;
const std::string styleURL;
const LatLngBounds bounds;

View File

@@ -23,20 +23,20 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
}
}
std::vector<TileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const SourceInfo& info) const {
double minZ = std::max<double>(coveringZoomLevel(minZoom, type, tileSize), info.minZoom);
double maxZ = std::min<double>(coveringZoomLevel(maxZoom, type, tileSize), info.maxZoom);
std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const SourceInfo& info) const {
double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), info.minZoom);
double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), info.maxZoom);
assert(minZ >= 0);
assert(maxZ >= 0);
assert(minZ < std::numeric_limits<uint8_t>::max());
assert(maxZ < std::numeric_limits<uint8_t>::max());
std::vector<TileID> result;
std::vector<CanonicalTileID> result;
for (uint8_t z = minZ; z <= maxZ; z++) {
for (const auto& tile : mbgl::tileCover(bounds, z, z)) {
result.push_back(tile.normalized());
for (const auto& tile : util::tileCover(bounds, z)) {
result.emplace_back(tile.canonical);
}
}

View File

@@ -4,7 +4,6 @@
#include <mbgl/util/io.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/platform/log.hpp>
#include "sqlite3.hpp"

View File

@@ -0,0 +1,10 @@
#include <mbgl/algorithm/update_renderables_impl.hpp>
#include <mbgl/style/style_layer.hpp>
namespace mbgl {
namespace algorithm {
//template void updateRenderables(StyleLayer& layer, const uint8_t z);
} // namespace algorithm
} // namespace mbgl

View File

@@ -0,0 +1,15 @@
#ifndef MBGL_ALGORITHM_UPDATE_RENDERABLES
#define MBGL_ALGORITHM_UPDATE_RENDERABLES
#include <cstdint>
namespace mbgl {
namespace algorithm {
template <typename Layer>
void updateRenderables(Layer& layer, const uint8_t z);
} // namespace algorithm
} // namespace mbgl
#endif

View File

@@ -0,0 +1,93 @@
#ifndef MBGL_ALGORITHM_UPDATE_RENDERABLES_IMPL
#define MBGL_ALGORITHM_UPDATE_RENDERABLES_IMPL
#include <mbgl/algorithm/update_renderables.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <map>
namespace mbgl {
namespace algorithm {
namespace {
template <typename DataTiles, typename Renderables>
bool tryTile(const UnwrappedTileID& renderTileID,
const OverscaledTileID& dataTileID,
const DataTiles& dataTiles,
Renderables& renderables) {
if (renderables.find(renderTileID) == renderables.end()) {
const auto it = dataTiles.find(dataTileID);
if (it == dataTiles.end() || !it->second->isReady()) {
return false;
}
using Renderable = typename Renderables::mapped_type;
renderables.emplace(renderTileID, Renderable{ renderTileID, *it->second });
}
return true;
}
} // namespace
template <typename Renderable, typename DataTiles, typename IdealTileIDs, typename SourceInfo>
std::map<UnwrappedTileID, Renderable> updateRenderables(const DataTiles& dataTiles,
const IdealTileIDs& idealTileIDs,
const SourceInfo& info,
const uint8_t z) {
std::map<UnwrappedTileID, Renderable> renderables;
// for (all in the set of ideal tiles of the source) {
for (const auto& renderTileID : idealTileIDs) {
assert(renderTileID.canonical.z >= info.minZoom);
assert(renderTileID.canonical.z <= info.maxZoom);
assert(z >= renderTileID.canonical.z);
const auto wrap = renderTileID.wrap;
const OverscaledTileID dataTileID(z, renderTileID.canonical);
// if (source has the tile and bucket is loaded) {
if (!tryTile(renderTileID, dataTileID, dataTiles, renderables)) {
// The source doesn't have the tile, or the bucket isn't loaded.
bool covered = true;
int32_t overscaledZ = z + 1;
if (overscaledZ > info.maxZoom) {
// We're looking for an overzoomed child tile.
const auto childDataTileID = dataTileID.scaledTo(overscaledZ);
if (!tryTile(renderTileID, childDataTileID, dataTiles, renderables)) {
covered = false;
}
} else {
// Check all four actual child tiles.
for (const auto& childTileID : dataTileID.canonical.children()) {
const OverscaledTileID childDataTileID(overscaledZ, childTileID);
const UnwrappedTileID childRenderTileID(wrap, childTileID);
if (!tryTile(childRenderTileID, childDataTileID, dataTiles, renderables)) {
// At least one child tile doesn't exist, so we are going to look for
// parents as well.
covered = false;
}
}
}
if (!covered) {
// We couldn't find child tiles that entirely cover the ideal tile.
for (overscaledZ = z - 1; overscaledZ >= info.minZoom; --overscaledZ) {
const auto parentDataTileID = dataTileID.scaledTo(overscaledZ);
const auto parentRenderTileID = parentDataTileID.unwrapTo(renderTileID.wrap);
if (tryTile(parentRenderTileID, parentDataTileID, dataTiles, renderables)) {
// Break parent tile ascent, since we found one.
break;
}
}
}
}
}
return renderables;
}
} // namespace algorithm
} // namespace mbgl
#endif

View File

@@ -1,71 +0,0 @@
#include <mbgl/map/tile_id.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/constants.hpp>
#include <cassert>
namespace mbgl {
float TileID::pixelsToTileUnits(float pixelValue, float zoom) const {
return pixelValue * (util::EXTENT / (util::tileSize * overscaleFactor() * std::pow(2, zoom - z)));
}
TileID TileID::parent(uint8_t parent_z, uint8_t sourceMaxZoom) const {
assert(parent_z < z);
auto newX = x;
auto newY = y;
for (auto newZ = z; newZ > parent_z; newZ--) {
if (newZ > sourceMaxZoom) {
// the id represents an overscaled tile, return the same coordinates with a lower z
// do nothing
} else {
newX = newX / 2;
newY = newY / 2;
}
}
return TileID{parent_z, newX, newY, parent_z > sourceMaxZoom ? sourceMaxZoom : parent_z};
}
std::forward_list<TileID> TileID::children(uint8_t sourceMaxZoom) const {
auto childZ = z + 1;
std::forward_list<TileID> child_ids;
if (z >= sourceMaxZoom) {
// return a single tile id representing a an overscaled tile
child_ids.emplace_front(childZ, x, y, sourceMaxZoom);
} else {
auto childX = x * 2;
auto childY = y * 2;
child_ids.emplace_front(childZ, childX, childY, childZ);
child_ids.emplace_front(childZ, childX + 1, childY, childZ);
child_ids.emplace_front(childZ, childX, childY + 1, childZ);
child_ids.emplace_front(childZ, childX + 1, childY + 1, childZ);
}
return child_ids;
}
TileID TileID::normalized() const {
int32_t dim = std::pow(2, sourceZ);
int32_t nx = x, ny = y;
while (nx < 0) nx += dim;
while (nx >= dim) nx -= dim;
return TileID { z, nx, ny, sourceZ};
}
bool TileID::isChildOf(const TileID &parent_id) const {
if (parent_id.z >= z || parent_id.w != w) {
return false;
}
int32_t scale = std::pow(2, z - parent_id.z);
return parent_id.x == ((x < 0 ? x - scale + 1 : x) / scale) &&
parent_id.y == y / scale;
}
TileID::operator std::string() const {
return util::toString(z) + "/" + util::toString(x) + "/" + util::toString(y);
}
} // namespace mbgl

View File

@@ -1,71 +0,0 @@
#ifndef MBGL_MAP_TILE_ID
#define MBGL_MAP_TILE_ID
#include <cstdint>
#include <cmath>
#include <string>
#include <functional>
#include <forward_list>
#include <limits>
namespace mbgl {
class TileID {
public:
const int16_t w = 0;
const uint8_t z;
const int32_t x;
const int32_t y;
const uint8_t sourceZ;
explicit TileID(uint8_t z_, int32_t x_, int32_t y_, uint8_t sourceZ_)
: w((x_ < 0 ? x_ - (1 << z_) + 1 : x_) / (1 << z_)), z(z_), x(x_), y(y_),
sourceZ(sourceZ_) {}
uint64_t to_uint64() const {
return ((std::pow(2, z) * y + x) * 32) + z;
}
uint32_t overscaleFactor() const {
return static_cast<uint32_t>(1) << (z - sourceZ);
}
bool operator==(const TileID& rhs) const {
return w == rhs.w && z == rhs.z && x == rhs.x && y == rhs.y;
}
bool operator!=(const TileID& rhs) const {
return !operator==(rhs);
}
bool operator<(const TileID& rhs) const {
if (w != rhs.w) return w < rhs.w;
if (z != rhs.z) return z < rhs.z;
if (x != rhs.x) return x < rhs.x;
return y < rhs.y;
}
float pixelsToTileUnits(float pixelValue, float zoom) const;
TileID parent(uint8_t z, uint8_t sourceMaxZoom) const;
TileID normalized() const;
std::forward_list<TileID>
children(uint8_t sourceMaxZoom = std::numeric_limits<uint8_t>::max()) const;
bool isChildOf(const TileID&) const;
operator std::string() const;
};
} // namespace mbgl
namespace std {
template <>
struct hash<mbgl::TileID> {
typedef mbgl::TileID argument_type;
typedef std::size_t result_type;
result_type operator()(const mbgl::TileID& id) const {
return std::hash<uint64_t>()(id.to_uint64());
}
};
} // namespace std
#endif

View File

@@ -153,8 +153,7 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
for (const auto& source : sources) {
if (source->type == SourceType::Vector || source->type == SourceType::GeoJSON ||
source->type == SourceType::Annotations) {
auto renderables = source->getRenderables();
generator.update(renderables);
source->updateClipIDs(generator);
}
source->updateMatrices(projMatrix, state);
}
@@ -253,7 +252,6 @@ void Painter::renderPass(RenderPass pass_,
layer.as<CustomLayer>()->render(state);
config.setDirty();
} else {
assert(item.tile->data);
MBGL_DEBUG_GROUP(layer.id + " - " + util::toString(item.tile->id));
if (item.bucket->needsClipping()) {
setClipping(item.tile->clip);

View File

@@ -55,12 +55,11 @@ void Painter::renderBackground(const BackgroundLayer& layer) {
config.depthMask = GL_FALSE;
setDepthSublayer(0);
auto tileIDs = tileCover(state, state.getIntegerZoom(), state.getIntegerZoom());
auto tileIDs = util::tileCover(state, state.getIntegerZoom());
for (auto &id: tileIDs) {
for (auto& id : tileIDs) {
mat4 vtxMatrix;
const UnwrappedTileID tileID(id.z, id.x, id.y);
state.matrixFor(vtxMatrix, tileID);
state.matrixFor(vtxMatrix, id);
matrix::multiply(vtxMatrix, projMatrix, vtxMatrix);
if (isPatterned) {
@@ -84,11 +83,15 @@ void Painter::renderBackground(const BackgroundLayer& layer) {
1.0f / id.pixelsToTileUnits(imageSizeScaledB[1], state.getIntegerZoom())
}};
float offsetAx = (std::fmod(util::tileSize, imageSizeScaledA[0]) * id.x) / (float)imageSizeScaledA[0];
float offsetAy = (std::fmod(util::tileSize, imageSizeScaledA[1]) * id.y) / (float)imageSizeScaledA[1];
float offsetAx = (std::fmod(util::tileSize, imageSizeScaledA[0]) * id.canonical.x) /
(float)imageSizeScaledA[0];
float offsetAy = (std::fmod(util::tileSize, imageSizeScaledA[1]) * id.canonical.y) /
(float)imageSizeScaledA[1];
float offsetBx = (std::fmod(util::tileSize, imageSizeScaledB[0]) * id.x) / (float)imageSizeScaledB[0];
float offsetBy = (std::fmod(util::tileSize, imageSizeScaledB[1]) * id.y) / (float)imageSizeScaledB[1];
float offsetBx = (std::fmod(util::tileSize, imageSizeScaledB[0]) * id.canonical.x) /
(float)imageSizeScaledB[0];
float offsetBy = (std::fmod(util::tileSize, imageSizeScaledB[1]) * id.canonical.y) /
(float)imageSizeScaledB[1];
patternShader->u_offset_a = std::array<float, 2>{{offsetAx, offsetAy}};
patternShader->u_offset_b = std::array<float, 2>{{offsetBx, offsetBy}};

View File

@@ -10,11 +10,10 @@ using namespace mbgl;
void Painter::renderTileDebug(const Tile& tile) {
MBGL_DEBUG_GROUP(std::string { "debug " } + util::toString(tile.id));
assert(tile.data);
if (frame.debugOptions != MapDebugOptions::NoDebug) {
setClipping(tile.clip);
if (frame.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
renderDebugText(*tile.data, tile.matrix);
renderDebugText(tile.data, tile.matrix);
}
if (frame.debugOptions & MapDebugOptions::TileBorders) {
renderDebugFrame(tile.matrix);

View File

@@ -26,6 +26,8 @@
#include <mbgl/style/style_parser.hpp>
#include <mbgl/gl/debugging.hpp>
#include <mbgl/algorithm/update_renderables_impl.hpp>
#include <mapbox/geojsonvt.hpp>
#include <mapbox/geojsonvt/convert.hpp>
@@ -55,8 +57,8 @@ Source::~Source() = default;
bool Source::isLoaded() const {
if (!loaded) return false;
for (const auto& tile : tiles) {
if (tile.second->data->getState() != TileData::State::parsed) {
for (const auto& pair : tileDataMap) {
if (pair.second->getState() != TileData::State::parsed) {
return false;
}
}
@@ -148,7 +150,6 @@ void Source::load(FileSource& fileSource) {
if (reloadTiles) {
// Tile information changed because we got new GeoJSON data, or a new tile URL.
tilePtrs.clear();
tileDataMap.clear();
tiles.clear();
cache.clear();
@@ -161,196 +162,66 @@ void Source::load(FileSource& fileSource) {
}
void Source::updateMatrices(const mat4 &projMatrix, const TransformState &transform) {
for (const auto& pair : tiles) {
auto& tile = *pair.second;
for (auto& pair : tiles) {
auto& tile = pair.second;
transform.matrixFor(tile.matrix, tile.id);
matrix::multiply(tile.matrix, projMatrix, tile.matrix);
}
}
void Source::finishRender(Painter &painter) {
for (const auto& pair : tiles) {
Tile &tile = *pair.second;
for (auto& pair : tiles) {
auto& tile = pair.second;
painter.renderTileDebug(tile);
}
}
std::map<UnwrappedTileID, Renderable> Source::getRenderables() const {
std::map<UnwrappedTileID, Renderable> renderables;
for (const auto& pair : tiles) {
auto& tile = *pair.second;
if (tile.data->isReady() && tile.data->hasData()) {
renderables.emplace(tile.id, Renderable{ tile.clip });
}
}
return renderables;
const std::map<UnwrappedTileID, Tile>& Source::getTiles() const {
return tiles;
}
const std::vector<Tile*>& Source::getTiles() const {
return tilePtrs;
}
TileData::State Source::hasTile(const TileID& tileID) {
auto it = tiles.find(tileID);
if (it != tiles.end()) {
Tile& tile = *it->second;
if (it->first == tileID && tile.data) {
return tile.data->getState();
}
}
return TileData::State::invalid;
}
bool Source::handlePartialTile(const TileID& tileID) {
const OverscaledTileID overscaledTileID{
tileID.z, UnwrappedTileID{ tileID.sourceZ, tileID.x, tileID.y }.canonical
};
auto it = tileDataMap.find(overscaledTileID);
if (it == tileDataMap.end()) {
return true;
}
auto tileData = it->second.lock();
if (!tileData) {
return true;
std::unique_ptr<TileData> Source::createTile(const OverscaledTileID& overscaledTileID,
const StyleUpdateParameters& parameters) {
std::unique_ptr<TileData> data = cache.get(overscaledTileID);
if (data) {
return data;
}
auto callback = std::bind(&Source::tileLoadingCallback, this, overscaledTileID,
std::placeholders::_1, false);
std::placeholders::_1, true);
return tileData->parsePending(callback);
}
// If we don't find working tile data, we're just going to load it.
if (type == SourceType::Raster) {
data = std::make_unique<RasterTileData>(overscaledTileID, parameters.pixelRatio,
info->tiles.at(0), parameters.texturePool,
parameters.worker, parameters.fileSource, callback);
} else {
std::unique_ptr<GeometryTileMonitor> monitor;
TileData::State Source::addTile(const TileID& tileID, const StyleUpdateParameters& parameters) {
const TileData::State state = hasTile(tileID);
if (type == SourceType::Vector) {
monitor = std::make_unique<VectorTileMonitor>(overscaledTileID, parameters.pixelRatio, info->tiles.at(0), parameters.fileSource);
} else if (type == SourceType::Annotations) {
monitor = std::make_unique<AnnotationTileMonitor>(overscaledTileID, parameters.annotationManager);
} else if (type == SourceType::GeoJSON) {
monitor = std::make_unique<GeoJSONTileMonitor>(geojsonvt.get(), overscaledTileID);
} else {
Log::Warning(Event::Style, "Source type '%s' is not implemented", SourceTypeClass(type).c_str());
return nullptr;
}
if (state != TileData::State::invalid) {
return state;
data = std::make_unique<VectorTileData>(overscaledTileID, std::move(monitor), id,
parameters.style, parameters.mode, callback);
}
auto newTile = std::make_unique<Tile>(UnwrappedTileID{ tileID.sourceZ, tileID.x, tileID.y });
// We couldn't find the tile in the list. Create a new one.
// Try to find the associated TileData object.
const OverscaledTileID overscaledTileID{ tileID.z, newTile->id.canonical };
return data;
}
TileData* Source::getTileData(const OverscaledTileID& overscaledTileID) const {
auto it = tileDataMap.find(overscaledTileID);
if (it != tileDataMap.end()) {
// Create a shared_ptr handle. Note that this might be empty!
newTile->data = it->second.lock();
}
if (newTile->data && newTile->data->getState() == TileData::State::obsolete) {
// Do not consider the tile if it's already obsolete.
newTile->data.reset();
}
if (!newTile->data) {
newTile->data = cache.get(overscaledTileID);
}
if (!newTile->data) {
auto callback = std::bind(&Source::tileLoadingCallback, this, overscaledTileID,
std::placeholders::_1, true);
// If we don't find working tile data, we're just going to load it.
if (type == SourceType::Raster) {
newTile->data = std::make_shared<RasterTileData>(overscaledTileID,
parameters.pixelRatio,
info->tiles.at(0),
parameters.texturePool,
parameters.worker,
parameters.fileSource,
callback);
} else {
std::unique_ptr<GeometryTileMonitor> monitor;
if (type == SourceType::Vector) {
monitor = std::make_unique<VectorTileMonitor>(overscaledTileID, parameters.pixelRatio, info->tiles.at(0), parameters.fileSource);
} else if (type == SourceType::Annotations) {
monitor = std::make_unique<AnnotationTileMonitor>(overscaledTileID, parameters.annotationManager);
} else if (type == SourceType::GeoJSON) {
monitor = std::make_unique<GeoJSONTileMonitor>(geojsonvt.get(), overscaledTileID);
} else {
Log::Warning(Event::Style, "Source type '%s' is not implemented", SourceTypeClass(type).c_str());
return TileData::State::invalid;
}
newTile->data = std::make_shared<VectorTileData>(overscaledTileID,
std::move(monitor),
id,
parameters.style,
parameters.mode,
callback);
}
tileDataMap.emplace(newTile->data->id, newTile->data);
}
const auto newState = newTile->data->getState();
tiles.emplace(tileID, std::move(newTile));
return newState;
}
/**
* Recursively find children of the given tile that are already loaded.
*
* @param id The tile ID that we should find children for.
* @param maxCoveringZoom The maximum zoom level of children to look for.
* @param retain An object that we add the found tiles to.
*
* @return boolean Whether the children found completely cover the tile.
*/
bool Source::findLoadedChildren(const TileID& tileID, int32_t maxCoveringZoom, std::vector<TileID>& 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_back(child_id);
}
if (state != TileData::State::parsed) {
complete = false;
if (z < maxCoveringZoom) {
// Go further down the hierarchy to find more unloaded children.
findLoadedChildren(child_id, maxCoveringZoom, retain);
}
}
}
return complete;
}
/**
* Find a loaded parent of the given tile.
*
* @param id The tile ID that we should find children for.
* @param minCoveringZoom The minimum zoom level of parents to look for.
* @param retain An object that we add the found tiles to.
*
* @return boolean Whether a parent was found.
*/
void Source::findLoadedParent(const TileID& tileID, int32_t minCoveringZoom, std::vector<TileID>& retain, const StyleUpdateParameters& parameters) {
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_back(parent_id);
if (state == TileData::State::parsed) {
return;
}
}
const UnwrappedTileID unwrappedTileID(tileID.sourceZ, static_cast<uint32_t>(tileID.x),
static_cast<uint32_t>(tileID.y));
if (cache.has(OverscaledTileID{ tileID.z, unwrappedTileID.canonical })) {
addTile(parent_id, parameters);
retain.emplace_back(parent_id);
return;
}
return it->second.get();
} else {
return nullptr;
}
}
@@ -362,116 +233,89 @@ bool Source::update(const StyleUpdateParameters& parameters) {
}
// Determine the overzooming/underzooming amounts and required tiles.
std::vector<TileID> required;
int32_t zoom = coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize);
int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info->minZoom, info->maxZoom);
int32_t maxCoveringZoom = util::clamp<int32_t>(zoom + 1, info->minZoom, info->maxZoom);
int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize);
int32_t dataTileZoom = overscaledZoom;
if (zoom >= info->minZoom) {
const bool reparseOverscaled = type != SourceType::Raster;
std::vector<UnwrappedTileID> idealTiles;
if (overscaledZoom >= info->minZoom) {
int32_t idealZoom = std::min<int32_t>(info->maxZoom, overscaledZoom);
const auto actualZ = zoom;
if (zoom > info->maxZoom) {
zoom = info->maxZoom;
// Make sure we're not reparsing overzoomed raster tiles.
if (type == SourceType::Raster) {
dataTileZoom = idealZoom;
}
required = tileCover(parameters.transformState, zoom, reparseOverscaled ? actualZ : zoom);
}
idealTiles = util::tileCover(parameters.transformState, idealZoom);
}
// 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::vector<TileID> retain(required);
// Stores a list of all the data tiles that we're definitely going to retain. There are two
// kinds of tiles we need: the ideal tiles determined by the tile cover. They may not yet be in
// use because they're still loading. In addition to that, we also need to retain all tiles that
// we're actively using, e.g. as a replacement for tile that aren't loaded yet.
std::set<OverscaledTileID> retain;
// Add existing child/parent tiles if the actual tile is not yet loaded
for (const auto& tileID : required) {
TileData::State state = hasTile(tileID);
// Create all tiles that we definitely want to load
for (const auto& unwrappedTileID : idealTiles) {
const OverscaledTileID dataTileID(dataTileZoom, unwrappedTileID.canonical);
retain.emplace(dataTileID);
switch (state) {
case TileData::State::partial:
if (parameters.shouldReparsePartialTiles) {
if (!handlePartialTile(tileID)) {
allTilesUpdated = false;
}
}
break;
case TileData::State::invalid:
state = addTile(tileID, parameters);
break;
default:
break;
}
if (!TileData::isReadyState(state)) {
// The tile we require is not yet loaded. Try to find a parent or
// child tile that we already have.
// First, try to find existing child tiles that completely cover the
// missing tile.
bool complete = findLoadedChildren(tileID, maxCoveringZoom, retain);
// Then, if there are no complete child tiles, try to find existing
// parent tiles that completely cover the missing tile.
if (!complete) {
findLoadedParent(tileID, minCoveringZoom, retain, parameters);
auto it = tileDataMap.find(dataTileID);
if (it == tileDataMap.end()) {
if (auto data = createTile(dataTileID, parameters)) {
it = tileDataMap.emplace(dataTileID, std::move(data)).first;
}
}
}
if (type != SourceType::Raster && cache.getSize() == 0) {
size_t conservativeCacheSize = ((float)parameters.transformState.getWidth() / util::tileSize) *
((float)parameters.transformState.getHeight() / util::tileSize) *
(parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) *
0.5;
tiles = algorithm::updateRenderables<Tile>(tileDataMap, idealTiles, *info, overscaledZoom);
for (auto& pair : tiles) {
retain.emplace(pair.second.data.id);
}
if (type != SourceType::Raster && type != SourceType::Annotations && cache.getSize() == 0) {
size_t conservativeCacheSize =
((float)parameters.transformState.getWidth() / util::tileSize) *
((float)parameters.transformState.getHeight() / util::tileSize) *
(parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) *
0.5;
cache.setSize(conservativeCacheSize);
}
auto& tileCache = cache;
// Remove tiles that we definitely don't need, i.e. tiles that are not on
// the required list.
std::set<OverscaledTileID> retain_data;
util::erase_if(tiles, [this, &retain, &retain_data, &tileCache](auto &pair) {
const auto& tileID = pair.first;
Tile &tile = *pair.second;
bool obsolete = std::find(retain.begin(), retain.end(), tileID) == retain.end();
if (!obsolete) {
retain_data.insert(tile.data->id);
} else if (type != SourceType::Raster && tile.data->getState() == TileData::State::parsed) {
// Partially parsed tiles are never added to the cache because otherwise
// they never get updated if the go out from the viewport and the pending
// resources arrive.
tileCache.add(tile.data->id, tile.data);
}
return obsolete;
});
// Remove all the expired pointers from the set.
util::erase_if(tileDataMap, [&retain_data, &tileCache](auto &pair) {
const auto tileData = pair.second.lock();
if (!tileData) {
return true;
}
bool obsolete = retain_data.find(tileData->id) == retain_data.end();
if (obsolete) {
if (!tileCache.has(tileData->id)) {
tileData->cancel();
}
return true;
// Remove stale data tiles from the active set of tiles.
// This goes through the (sorted!) tileDataMap and retain set in lockstep and removes items from
// tileDataMap that don't have the corresponding key in the retain set.
auto dataIt = tileDataMap.begin();
auto retainIt = retain.begin();
while (dataIt != tileDataMap.end()) {
if (retainIt == retain.end() || dataIt->first < *retainIt) {
cache.add(dataIt->first, std::move(dataIt->second));
tileDataMap.erase(dataIt++);
} else {
return false;
if (!(*retainIt < dataIt->first)) {
++dataIt;
}
++retainIt;
}
});
}
updateTilePtrs();
for (auto& pair : tileDataMap) {
const auto& dataTileID = pair.first;
auto tileData = pair.second.get();
if (parameters.shouldReparsePartialTiles &&
tileData->getState() == TileData::State::partial) {
auto callback = std::bind(&Source::tileLoadingCallback, this, dataTileID,
std::placeholders::_1, false);
for (auto& tilePtr : tilePtrs) {
tilePtr->data->redoPlacement(
{ parameters.transformState.getAngle(), parameters.transformState.getPitch(), parameters.debugOptions & MapDebugOptions::Collision },
[this]() {
observer->onPlacementRedone();
});
if (!tileData->parsePending(callback)) {
allTilesUpdated = false;
}
} else {
tileData->redoPlacement({ parameters.transformState.getAngle(),
parameters.transformState.getPitch(),
parameters.debugOptions & MapDebugOptions::Collision },
[this]() { observer->onPlacementRedone(); });
}
}
updated = parameters.animationTime;
@@ -479,13 +323,6 @@ bool Source::update(const StyleUpdateParameters& parameters) {
return allTilesUpdated;
}
void Source::updateTilePtrs() {
tilePtrs.clear();
for (const auto& pair : tiles) {
tilePtrs.push_back(pair.second.get());
}
}
static Point<int16_t> coordinateToTilePoint(const CanonicalTileID& tileID, const TileCoordinate& coord) {
auto zoomedCoord = coord.zoomTo(tileID.z);
return {
@@ -499,7 +336,7 @@ static Point<int16_t> coordinateToTilePoint(const CanonicalTileID& tileID, const
}
struct TileQuery {
Tile* tile;
TileData& tileData;
GeometryCollection queryGeometry;
double tileSize;
double scale;
@@ -528,8 +365,8 @@ std::unordered_map<std::string, std::vector<Feature>> Source::queryRenderedFeatu
std::map<CanonicalTileID, TileQuery> tileQueries;
for (auto& tilePtr : tilePtrs) {
auto& tile = *tilePtr;
for (auto& tilePtr : tiles) {
auto& tile = tilePtr.second;
auto tileSpaceBoundsMin = coordinateToTilePoint(tile.id.canonical, { minX, minY, z });
auto tileSpaceBoundsMax = coordinateToTilePoint(tile.id.canonical, { maxX, maxY, z });
@@ -549,10 +386,10 @@ std::unordered_map<std::string, std::vector<Feature>> Source::queryRenderedFeatu
} else {
(void)zoom;
tileQueries.emplace(tile.id.canonical, TileQuery{
tilePtr,
tile.data,
{ tileSpaceQueryGeometry },
util::tileSize * tile.data->id.overscaleFactor(),
std::pow(2, zoom - tile.data->id.overscaledZ)
util::tileSize * tile.data.id.overscaleFactor(),
std::pow(2, zoom - tile.data.id.overscaledZ)
});
}
}
@@ -560,7 +397,7 @@ std::unordered_map<std::string, std::vector<Feature>> Source::queryRenderedFeatu
for (auto& it : tileQueries) {
auto& tileQuery = std::get<1>(it);
tileQuery.tile->data->queryRenderedFeatures(result, tileQuery.queryGeometry, bearing, tileQuery.tileSize, tileQuery.scale, layerIDs);
tileQuery.tileData.queryRenderedFeatures(result, tileQuery.queryGeometry, bearing, tileQuery.tileSize, tileQuery.scale, layerIDs);
}
return result;
@@ -586,7 +423,7 @@ void Source::tileLoadingCallback(const OverscaledTileID& tileID,
return;
}
util::ptr<TileData> tileData = it->second.lock();
auto& tileData = it->second;
if (!tileData) {
return;
}
@@ -606,8 +443,9 @@ void Source::dumpDebugLogs() const {
Log::Info(Event::General, "Source::id: %s", id.c_str());
Log::Info(Event::General, "Source::loaded: %d", loaded);
for (const auto& tile : tiles) {
tile.second->data->dumpDebugLogs();
for (const auto& pair : tileDataMap) {
auto& tileData = pair.second;
tileData->dumpDebugLogs();
}
}

View File

@@ -69,11 +69,17 @@ public:
// new data available that a tile in the "partial" state might be interested at.
bool update(const StyleUpdateParameters&);
template <typename ClipIDGenerator>
void updateClipIDs(ClipIDGenerator& generator) {
generator.update(tiles);
}
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void finishRender(Painter &painter);
std::map<UnwrappedTileID, Renderable> getRenderables() const;
const std::vector<Tile*>& getTiles() const;
const std::map<UnwrappedTileID, Tile>& getTiles() const;
TileData* getTileData(const OverscaledTileID&) const;
std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(
const std::vector<TileCoordinate>& queryGeometry,
@@ -94,16 +100,10 @@ public:
bool enabled = false;
private:
void tileLoadingCallback(const OverscaledTileID&,
std::exception_ptr,
bool isNewTile);
bool handlePartialTile(const TileID&);
bool findLoadedChildren(const TileID&, int32_t maxCoveringZoom, std::vector<TileID>& retain);
void findLoadedParent(const TileID&, int32_t minCoveringZoom, std::vector<TileID>& retain, const StyleUpdateParameters&);
void tileLoadingCallback(const OverscaledTileID&, std::exception_ptr, bool isNewTile);
TileData::State addTile(const TileID&, const StyleUpdateParameters&);
TileData::State hasTile(const TileID&);
void updateTilePtrs();
std::unique_ptr<TileData> createTile(const OverscaledTileID&,
const StyleUpdateParameters& parameters);
private:
std::unique_ptr<const SourceInfo> info;
@@ -113,9 +113,8 @@ private:
// Stores the time when this source was most recently updated.
TimePoint updated = TimePoint::min();
std::map<TileID, std::unique_ptr<Tile>> tiles;
std::vector<Tile*> tilePtrs;
std::map<OverscaledTileID, std::weak_ptr<TileData>> tileDataMap;
std::map<UnwrappedTileID, Tile> tiles;
std::map<OverscaledTileID, std::unique_ptr<TileData>> tileDataMap;
TileCache cache;
std::unique_ptr<AsyncRequest> req;

View File

@@ -282,9 +282,11 @@ RenderData Style::getRenderData() const {
continue;
}
for (auto tile : source->getTiles()) {
if (!tile->data || !tile->data->isReady())
for (auto& pair : source->getTiles()) {
auto& tile = pair.second;
if (!tile.data.isReady()) {
continue;
}
// We're not clipping symbol layers, so when we have both parents and children of symbol
// layers, we drop all children in favor of their parent to avoid duplicate labels.
@@ -295,7 +297,7 @@ RenderData Style::getRenderData() const {
// already a bucket from this layer that is a parent of this tile. Tiles are ordered
// by zoom level when we obtain them from getTiles().
for (auto it = result.order.rbegin(); it != result.order.rend() && (&it->layer == layer.get()); ++it) {
if (tile->data->id.isChildOf(it->tile->data->id)) {
if (tile.data.id.isChildOf(it->tile->data.id)) {
skip = true;
break;
}
@@ -305,9 +307,9 @@ RenderData Style::getRenderData() const {
}
}
auto bucket = tile->data->getBucket(*layer);
auto bucket = tile.data.getBucket(*layer);
if (bucket) {
result.order.emplace_back(*layer, tile, bucket);
result.order.emplace_back(*layer, &tile, bucket);
}
}
}

View File

@@ -3,7 +3,6 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/clip_id.hpp>
@@ -12,15 +11,20 @@ namespace mbgl {
class TileData;
struct box;
class Tile : private util::noncopyable {
class Tile {
public:
Tile(const UnwrappedTileID& id_) : id(id_) {
Tile(const UnwrappedTileID& id_, TileData& data_) : id(id_), data(data_) {
}
Tile(const Tile&) = delete;
Tile(Tile&&) = default;
Tile& operator=(const Tile&) = delete;
Tile& operator=(Tile&&) = default;
const UnwrappedTileID id;
TileData& data;
ClipID clip;
mat4 matrix;
util::ptr<TileData> data;
};
} // namespace mbgl

View File

@@ -17,10 +17,13 @@ void TileCache::setSize(size_t size_) {
assert(orderedKeys.size() <= size);
}
void TileCache::add(const OverscaledTileID& key, std::shared_ptr<TileData> data) {
void TileCache::add(const OverscaledTileID& key, std::unique_ptr<TileData> data) {
if (!data->isReady() || !size) {
return;
}
// insert new or query existing data
if (tiles.emplace(key, data).second) {
if (tiles.emplace(key, std::move(data)).second) {
// remove existing data key
orderedKeys.remove(key);
}
@@ -36,13 +39,13 @@ void TileCache::add(const OverscaledTileID& key, std::shared_ptr<TileData> data)
assert(orderedKeys.size() <= size);
};
std::shared_ptr<TileData> TileCache::get(const OverscaledTileID& key) {
std::unique_ptr<TileData> TileCache::get(const OverscaledTileID& key) {
std::shared_ptr<TileData> data;
std::unique_ptr<TileData> data;
auto it = tiles.find(key);
if (it != tiles.end()) {
data = it->second;
data = std::move(it->second);
tiles.erase(it);
orderedKeys.remove(key);
assert(data->isReady());

View File

@@ -17,13 +17,13 @@ public:
void setSize(size_t);
size_t getSize() const { return size; };
void add(const OverscaledTileID& key, std::shared_ptr<TileData> data);
std::shared_ptr<TileData> get(const OverscaledTileID& key);
void add(const OverscaledTileID& key, std::unique_ptr<TileData> data);
std::unique_ptr<TileData> get(const OverscaledTileID& key);
bool has(const OverscaledTileID& key);
void clear();
private:
std::map<OverscaledTileID, std::shared_ptr<TileData>> tiles;
std::map<OverscaledTileID, std::unique_ptr<TileData>> tiles;
std::list<OverscaledTileID> orderedKeys;
size_t size;

View File

@@ -3,6 +3,8 @@
#include <mbgl/util/interpolate.hpp>
#include <mbgl/map/transform_state.hpp>
#include <functional>
namespace mbgl {
namespace {
@@ -69,30 +71,36 @@ static void scanTriangle(const TileCoordinate& a, const TileCoordinate& b, const
} // namespace
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);
}
}
namespace util {
std::vector<TileID> tileCover(const TileCoordinate& tl,
const TileCoordinate& tr,
const TileCoordinate& br,
const TileCoordinate& bl,
const TileCoordinate& c,
int32_t z,
int32_t actualZ) {
int32_t tiles = 1 << z;
std::forward_list<mbgl::TileID> t;
namespace {
std::vector<UnwrappedTileID> tileCover(const TileCoordinate& tl,
const TileCoordinate& tr,
const TileCoordinate& br,
const TileCoordinate& bl,
const TileCoordinate& c,
int32_t z) {
assert(tl.z == z);
assert(tr.z == z);
assert(br.z == z);
assert(bl.z == z);
assert(c.z == z);
const int32_t tiles = 1 << z;
struct ID {
int32_t x, y;
double sqDist;
};
std::vector<ID> t;
auto scanLine = [&](int32_t x0, int32_t x1, int32_t y) {
int32_t x;
if (y >= 0 && y <= tiles) {
for (x = x0; x < x1; ++x) {
t.emplace_front(actualZ, x, y, z);
const auto dx = x + 0.5 - c.x, dy = y + 0.5 - c.y;
t.emplace_back(ID{ x, y, dx * dx + dy * dy });
}
}
};
@@ -104,19 +112,36 @@ std::vector<TileID> tileCover(const TileCoordinate& tl,
scanTriangle(tl, tr, br, 0, tiles, scanLine);
scanTriangle(br, bl, tl, 0, tiles, scanLine);
t.sort();
t.unique();
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);
// Sort first by distance, then by x/y.
std::sort(t.begin(), t.end(), [](const ID& a, const ID& b) {
return (a.sqDist != b.sqDist) ? (a.sqDist < b.sqDist)
: ((a.x != b.x) ? (a.x < b.x) : (a.y < b.y));
});
return std::vector<TileID>(t.begin(), t.end());
// Erase duplicate tile IDs (they typically occur at the common side of both triangles).
t.erase(std::unique(t.begin(), t.end(), [](const ID& a, const ID& b) {
return a.x == b.x && a.y == b.y;
}), t.end());
std::vector<UnwrappedTileID> result;
for (const auto& id : t) {
result.emplace_back(z, id.x, id.y);
}
return result;
}
std::vector<TileID> tileCover(const LatLngBounds& bounds_, int32_t z, int32_t actualZ) {
} // namespace
int32_t coveringZoomLevel(double zoom, SourceType type, uint16_t size) {
zoom += std::log(util::tileSize / size) / std::log(2);
if (type == SourceType::Raster || type == SourceType::Video) {
return ::round(zoom);
} else {
return std::floor(zoom);
}
}
std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) {
if (bounds_.isEmpty() ||
bounds_.south() > util::LATITUDE_MAX ||
bounds_.north() < -util::LATITUDE_MAX) {
@@ -134,10 +159,10 @@ std::vector<TileID> tileCover(const LatLngBounds& bounds_, int32_t z, int32_t ac
TileCoordinate::fromLatLng(state, z, bounds.southeast()),
TileCoordinate::fromLatLng(state, z, bounds.southwest()),
TileCoordinate::fromLatLng(state, z, bounds.center()),
z, actualZ);
z);
}
std::vector<TileID> tileCover(const TransformState& state, int32_t z, int32_t actualZ) {
std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) {
const double w = state.getWidth();
const double h = state.getHeight();
return tileCover(
@@ -146,7 +171,8 @@ std::vector<TileID> tileCover(const TransformState& state, int32_t z, int32_t ac
TileCoordinate::fromScreenCoordinate(state, z, { w, h }),
TileCoordinate::fromScreenCoordinate(state, z, { 0, h }),
TileCoordinate::fromScreenCoordinate(state, z, { w/2, h/2 }),
z, actualZ);
z);
}
} // namespace util
} // namespace mbgl

View File

@@ -1,7 +1,7 @@
#ifndef MBGL_UTIL_TILE_COVER
#define MBGL_UTIL_TILE_COVER
#include <mbgl/map/tile_id.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/util/tile_coordinate.hpp>
@@ -12,11 +12,14 @@ namespace mbgl {
class TransformState;
class LatLngBounds;
namespace util {
int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize);
std::vector<TileID> tileCover(const TransformState&, int32_t z, int32_t actualZ);
std::vector<TileID> tileCover(const LatLngBounds&, int32_t z, int32_t actualZ);
std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z);
std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z);
} // namespace util
} // namespace mbgl
#endif

60
test/algorithm/mock.hpp Normal file
View File

@@ -0,0 +1,60 @@
#ifndef MBGL_TEST_MOCK
#define MBGL_TEST_MOCK
#include <cstdint>
#include <string>
#include <memory>
#include <set>
#include <map>
#include <mbgl/tile/tile_id.hpp>
struct MockSourceInfo {
uint8_t maxZoom = 16;
uint8_t minZoom = 0;
};
struct MockTileData;
struct MockRenderable {
MockRenderable(mbgl::UnwrappedTileID id_, MockTileData& data_) : id(id_), data(data_) {}
const mbgl::UnwrappedTileID id;
MockTileData& data;
bool operator==(const MockRenderable& rhs) const {
return &data == &rhs.data;
}
};
::std::ostream& operator<<(::std::ostream& os, const MockRenderable&) {
return os << "Renderable{}";
}
struct MockSource {
MockSourceInfo info;
std::map<mbgl::OverscaledTileID, std::unique_ptr<MockTileData>> dataTiles;
std::set<mbgl::UnwrappedTileID> idealTiles;
std::map<mbgl::UnwrappedTileID, MockRenderable> renderables;
// Test API
inline MockTileData* createTileData(const mbgl::OverscaledTileID& tileID);
};
struct MockBucket {};
struct MockTileData {
bool isReady() {
return ready;
}
bool ready = false;
};
MockTileData* MockSource::createTileData(const mbgl::OverscaledTileID& tileID) {
// Replace the existing MockTileData object, if any.
return (dataTiles[tileID] = std::make_unique<MockTileData>()).get();
}
#endif

View File

@@ -0,0 +1,367 @@
#include <mbgl/test/util.hpp>
#include "mock.hpp"
#include <mbgl/algorithm/update_renderables_impl.hpp>
using namespace mbgl;
TEST(UpdateRenderables, SingleTile) {
MockSource source;
source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 1 });
// Make sure that we're getting the tile back.
auto tile_1_1_1 = source.createTileData(OverscaledTileID{ 1, 1, 1 });
tile_1_1_1->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } },
}),
source.renderables);
// Check a repeated render with the same data.
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } },
}),
source.renderables);
// Insert a tile we don't have data for.
source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 1 });
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } },
}),
source.renderables);
// Now insert the missing tile and check that we're rendering it.
auto tile_1_0_1 = source.createTileData(OverscaledTileID{ 1, 0, 1 });
tile_1_0_1->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } },
{ { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } },
}),
source.renderables);
// Insert another tile, and another bucket that has a different name and check that we're not
// using it.
source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 });
auto tile_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, 0 });
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } },
{ { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } },
}),
source.renderables);
// Then, add the bucket and check that it's getting used.
tile_1_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 0, 0 }, MockRenderable{ { 1, 0, 0 }, *tile_1_0_0 } },
{ { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } },
{ { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } },
}),
source.renderables);
}
TEST(UpdateRenderables, UseParentTile) {
MockSource source;
source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 1 });
source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 0 });
source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 1 });
// Make sure that we're getting the tile back.
auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 });
tile_0_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } },
}),
source.renderables);
}
TEST(UpdateRenderables, DontUseWrongParentTile) {
MockSource source;
source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 });
auto tile_1_1_0 = source.createTileData(OverscaledTileID{ 1, 1, 0 });
tile_1_1_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 2);
EXPECT_EQ(decltype(source.renderables)({}), source.renderables);
// Add a new child tile and check that it is now used.
source.idealTiles.emplace(UnwrappedTileID{ 2, 2, 0 });
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 2);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 1, 0 }, MockRenderable{ { 1, 1, 0 }, *tile_1_1_0 } },
}),
source.renderables);
}
TEST(UpdateRenderables, UseParentTileWhenChildNotReady) {
MockSource source;
source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 1 });
auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 });
tile_0_0_0->ready = true;
auto tile_1_0_1 = source.createTileData(OverscaledTileID{ 1, 0, 1 });
// Don't create bucket.
// Make sure that it renders the parent tile.
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } },
}),
source.renderables);
// Now insert the bucket and make sure we're now using the matching tile
tile_1_0_1->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } },
}),
source.renderables);
}
TEST(UpdateRenderables, UseOverlappingParentTile) {
MockSource source;
source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 });
source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 1 });
auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 });
tile_0_0_0->ready = true;
auto tile_1_0_1 = source.createTileData(OverscaledTileID{ 1, 0, 1 });
tile_1_0_1->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } },
{ { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } },
}),
source.renderables);
}
TEST(UpdateRenderables, UseChildTiles) {
MockSource source;
source.idealTiles.emplace(UnwrappedTileID{ 0, 0, 0 });
auto tile_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, 0 });
tile_1_0_0->ready = true;
auto tile_1_1_0 = source.createTileData(OverscaledTileID{ 1, 1, 0 });
tile_1_1_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 0);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 0, 0 }, MockRenderable{ { 1, 0, 0 }, *tile_1_0_0 } },
{ { 1, 1, 0 }, MockRenderable{ { 1, 1, 0 }, *tile_1_1_0 } },
}),
source.renderables);
}
TEST(UpdateRenderables, PreferChildTiles) {
MockSource source;
source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 });
auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 });
tile_0_0_0->ready = true;
auto tile_2_0_0 = source.createTileData(OverscaledTileID{ 2, 0, 0 });
tile_2_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } },
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } },
}),
source.renderables);
// Now add more children to cover the ideal tile fully, until it is covered fully, and verify
// that the parent doesn't get rendered.
auto tile_2_0_1 = source.createTileData(OverscaledTileID{ 2, 0, 1 });
tile_2_0_1->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } },
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } },
{ { 2, 0, 1 }, MockRenderable{ { 2, 0, 1 }, *tile_2_0_1 } },
}),
source.renderables);
auto tile_2_1_0 = source.createTileData(OverscaledTileID{ 2, 1, 0 });
tile_2_1_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } },
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } },
{ { 2, 0, 1 }, MockRenderable{ { 2, 0, 1 }, *tile_2_0_1 } },
{ { 2, 1, 0 }, MockRenderable{ { 2, 1, 0 }, *tile_2_1_0 } },
}),
source.renderables);
// Adding the last child tile covers 1/0/0 fully, so we don't need 0/0/0 anymore.
auto tile_2_1_1 = source.createTileData(OverscaledTileID{ 2, 1, 1 });
tile_2_1_1->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } },
{ { 2, 0, 1 }, MockRenderable{ { 2, 0, 1 }, *tile_2_0_1 } },
{ { 2, 1, 0 }, MockRenderable{ { 2, 1, 0 }, *tile_2_1_0 } },
{ { 2, 1, 1 }, MockRenderable{ { 2, 1, 1 }, *tile_2_1_1 } },
}),
source.renderables);
}
TEST(UpdateRenderables, UseParentAndChildTiles) {
MockSource source;
source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 });
auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 });
tile_0_0_0->ready = true;
auto tile_2_0_0 = source.createTileData(OverscaledTileID{ 2, 0, 0 });
tile_2_0_0->ready = true;
// Check that it uses the child tile, but not the parent tile.
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } },
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } },
}),
source.renderables);
// Then, remove the child tile and check that it now uses the parent tile.
source.dataTiles.erase(OverscaledTileID{ 2, 0, 0 });
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 1);
EXPECT_EQ(decltype(source.renderables)({
{ { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } },
}),
source.renderables);
}
TEST(UpdateRenderables, DontUseTilesLowerThanMinzoom) {
MockSource source;
source.info.minZoom = 2;
source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 });
auto tile_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, 0 });
tile_1_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 2);
EXPECT_EQ(decltype(source.renderables)({}), source.renderables);
}
TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) {
MockSource source;
source.info.maxZoom = 2;
source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 });
// Add a child tile (that should never occur in practice) and make sure it's not selected.
auto tile_3_3_0_0 = source.createTileData(OverscaledTileID{ 3, 0, 0 });
tile_3_3_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 2);
EXPECT_EQ(decltype(source.renderables)({}), source.renderables);
// Only add a non-overzoomed ("parent") tile at first.
auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } });
tile_2_2_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 3);
EXPECT_EQ(decltype(source.renderables)({
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_2_0_0 } },
}),
source.renderables);
// Then add the overzoomed tile matching the zoom level we're rendering.
auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } });
tile_3_2_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 3);
EXPECT_EQ(decltype(source.renderables)({
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_3_2_0_0 } },
}),
source.renderables);
// Check that it's switching back to the tile that has the matching overzoom value.
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 2);
EXPECT_EQ(decltype(source.renderables)({
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_2_0_0 } },
}),
source.renderables);
// Now remove the best match.
source.dataTiles.erase(OverscaledTileID{ 2, { 2, 0, 0 } });
tile_2_2_0_0 = nullptr;
// Use the overzoomed tile even though it doesn't match the zoom level.
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 2);
EXPECT_EQ(decltype(source.renderables)({
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_3_2_0_0 } },
}),
source.renderables);
}
TEST(UpdateRenderables, AscendToNonOverzoomedTiles) {
MockSource source;
source.info.maxZoom = 2;
source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 });
// Add a matching overzoomed tile and make sure it gets selected.
auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } });
tile_3_2_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 3);
EXPECT_EQ(decltype(source.renderables)({
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_3_2_0_0 } },
}),
source.renderables);
// Then, swap it with a non-overzoomed tile.
source.dataTiles.erase(OverscaledTileID{ 3, { 2, 0, 0 } });
tile_3_2_0_0 = nullptr;
auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } });
tile_2_2_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 3);
EXPECT_EQ(decltype(source.renderables)({
{ { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_2_0_0 } },
}),
source.renderables);
// Then, swap it with a parent tile.
source.dataTiles.erase(OverscaledTileID{ 2, { 2, 0, 0 } });
tile_2_2_0_0 = nullptr;
auto tile_1_1_0_0 = source.createTileData(OverscaledTileID{ 1, { 1, 0, 0 } });
tile_1_1_0_0->ready = true;
source.renderables = algorithm::updateRenderables<MockRenderable>(
source.dataTiles, source.idealTiles, source.info, 3);
EXPECT_EQ(decltype(source.renderables)({
{ { 1, 0, 0 }, MockRenderable{ { 1, 0, 0 }, *tile_1_1_0_0 } },
}),
source.renderables);
}

View File

@@ -1,50 +0,0 @@
#include <iostream>
#include <mbgl/test/util.hpp>
#include <mbgl/map/tile_id.hpp>
using namespace mbgl;
TEST(Variant, isChild) {
ASSERT_TRUE(TileID(1, 0, 0, 1).isChildOf(TileID(0, 0, 0, 0)));
ASSERT_TRUE(TileID(1, 1, 0, 1).isChildOf(TileID(0, 0, 0, 0)));
ASSERT_TRUE(TileID(1, 2, 0, 1).isChildOf(TileID(0, 1, 0, 0)));
ASSERT_TRUE(TileID(1, 3, 0, 1).isChildOf(TileID(0, 1, 0, 0)));
ASSERT_TRUE(TileID(1, 4, 0, 1).isChildOf(TileID(0, 2, 0, 0)));
ASSERT_TRUE(TileID(1, 5, 0, 1).isChildOf(TileID(0, 2, 0, 0)));
ASSERT_TRUE(TileID(2, 0, 0, 2).isChildOf(TileID(0, 0, 0, 0)));
ASSERT_TRUE(TileID(2, 8, 0, 2).isChildOf(TileID(0, 2, 0, 0)));
ASSERT_TRUE(TileID(2, 9, 0, 2).isChildOf(TileID(0, 2, 0, 0)));
ASSERT_TRUE(TileID(2, 10, 0, 2).isChildOf(TileID(0, 2, 0, 0)));
ASSERT_TRUE(TileID(2, 11, 0, 2).isChildOf(TileID(0, 2, 0, 0)));
ASSERT_TRUE(TileID(2, 12, 0, 2).isChildOf(TileID(0, 3, 0, 0)));
ASSERT_TRUE(TileID(2, 13, 0, 2).isChildOf(TileID(0, 3, 0, 0)));
ASSERT_TRUE(TileID(1, -1, 0, 1).isChildOf(TileID(0, -1, 0, 0)));
ASSERT_TRUE(TileID(1, -2, 0, 1).isChildOf(TileID(0, -1, 0, 0)));
ASSERT_TRUE(TileID(1, -3, 0, 1).isChildOf(TileID(0, -2, 0, 0)));
ASSERT_TRUE(TileID(1, -4, 0, 1).isChildOf(TileID(0, -2, 0, 0)));
ASSERT_TRUE(TileID(2, -1, 0, 2).isChildOf(TileID(0, -1, 0, 0)));
ASSERT_TRUE(TileID(2, -2, 0, 2).isChildOf(TileID(0, -1, 0, 0)));
ASSERT_TRUE(TileID(2, -3, 0, 2).isChildOf(TileID(0, -1, 0, 0)));
ASSERT_TRUE(TileID(2, -4, 0, 2).isChildOf(TileID(0, -1, 0, 0)));
ASSERT_TRUE(TileID(2, -5, 0, 2).isChildOf(TileID(0, -2, 0, 0)));
ASSERT_TRUE(TileID(2, -6, 0, 2).isChildOf(TileID(0, -2, 0, 0)));
ASSERT_TRUE(TileID(2, -7, 0, 2).isChildOf(TileID(0, -2, 0, 0)));
ASSERT_TRUE(TileID(2, -8, 0, 2).isChildOf(TileID(0, -2, 0, 0)));
ASSERT_FALSE(TileID(4, -16, 0, 4).isChildOf(TileID(0, -2, 0, 0)));
ASSERT_TRUE(TileID(4, -17, 0, 4).isChildOf(TileID(0, -2, 0, 0)));
ASSERT_TRUE(TileID(2, -1, 0, 2).isChildOf(TileID(1, -1, 0, 1)));
ASSERT_TRUE(TileID(2, -2, 0, 2).isChildOf(TileID(1, -1, 0, 1)));
ASSERT_TRUE(TileID(2, -3, 0, 2).isChildOf(TileID(1, -2, 0, 1)));
ASSERT_TRUE(TileID(2, -4, 0, 2).isChildOf(TileID(1, -2, 0, 1)));
ASSERT_TRUE(TileID(3, -1, 0, 3).isChildOf(TileID(1, -1, 0, 1)));
ASSERT_TRUE(TileID(3, -2, 0, 3).isChildOf(TileID(1, -1, 0, 1)));
ASSERT_TRUE(TileID(3, -3, 0, 3).isChildOf(TileID(1, -1, 0, 1)));
ASSERT_TRUE(TileID(3, -4, 0, 3).isChildOf(TileID(1, -1, 0, 1)));
ASSERT_TRUE(TileID(3, -5, 0, 3).isChildOf(TileID(1, -2, 0, 1)));
}

View File

@@ -1,25 +1,22 @@
#include <mbgl/storage/offline.hpp>
#include <mbgl/source/source_info.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <gtest/gtest.h>
using namespace mbgl;
static const LatLngBounds sanFrancisco = LatLngBounds::hull(
{ 37.6609, -122.5744 },
{ 37.8271, -122.3204 });
static const LatLngBounds sanFrancisco =
LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 });
static const LatLngBounds sanFranciscoWrapped = LatLngBounds::hull(
{ 37.6609, 238.5744 },
{ 37.8271, 238.3204 });
static const LatLngBounds sanFranciscoWrapped =
LatLngBounds::hull({ 37.6609, 238.5744 }, { 37.8271, 238.3204 });
TEST(OfflineTilePyramidRegionDefinition, TileCoverEmpty) {
OfflineTilePyramidRegionDefinition region("", LatLngBounds::empty(), 0, 20, 1.0);
SourceInfo info;
auto result = region.tileCover(SourceType::Vector, 512, info);
ASSERT_TRUE(result.empty());
EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, info));
}
TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomIntersection) {
@@ -27,47 +24,39 @@ TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomIntersection) {
SourceInfo info;
info.minZoom = 0;
auto resultIntersection = region.tileCover(SourceType::Vector, 512, info);
ASSERT_EQ(1, resultIntersection.size());
EXPECT_EQ((std::vector<CanonicalTileID>{ { 2, 0, 1 } }),
region.tileCover(SourceType::Vector, 512, info));
info.minZoom = 3;
auto resultNoIntersection = region.tileCover(SourceType::Vector, 512, info);
ASSERT_TRUE(resultNoIntersection.empty());
EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, info));
}
TEST(OfflineTilePyramidRegionDefinition, TileCoverTileSize) {
OfflineTilePyramidRegionDefinition region("", LatLngBounds::world(), 0, 0, 1.0);
SourceInfo info;
auto result512 = region.tileCover(SourceType::Vector, 512, info);
ASSERT_EQ(1, result512.size());
ASSERT_EQ(0, result512[0].z);
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
region.tileCover(SourceType::Vector, 512, info));
auto result256 = region.tileCover(SourceType::Vector, 256, info);
ASSERT_EQ(4, result256.size());
ASSERT_EQ(1, result256[0].z);
EXPECT_EQ((std::vector<CanonicalTileID>{ { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 } }),
region.tileCover(SourceType::Vector, 256, info));
}
TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomRounding) {
OfflineTilePyramidRegionDefinition region("", sanFrancisco, 0.6, 0.7, 1.0);
SourceInfo info;
auto resultVector = region.tileCover(SourceType::Vector, 512, info);
ASSERT_EQ(1, resultVector.size());
ASSERT_EQ(0, resultVector[0].z);
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
region.tileCover(SourceType::Vector, 512, info));
auto resultRaster = region.tileCover(SourceType::Raster, 512, info);
ASSERT_EQ(1, resultRaster.size());
ASSERT_EQ(1, resultRaster[0].z);
EXPECT_EQ((std::vector<CanonicalTileID>{ { 1, 0, 0 } }),
region.tileCover(SourceType::Raster, 512, info));
}
TEST(OfflineTilePyramidRegionDefinition, TileCoverWrapped) {
OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 0, 1.0);
SourceInfo info;
auto result = region.tileCover(SourceType::Vector, 512, info);
ASSERT_EQ(1, result.size());
ASSERT_EQ(0, result[0].z);
ASSERT_EQ(0, result[0].x);
ASSERT_EQ(0, result[0].y);
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
region.tileCover(SourceType::Vector, 512, info));
}

View File

@@ -35,6 +35,8 @@
'algorithm/covered_by_children.cpp',
'algorithm/generate_clip_ids.cpp',
'algorithm/mock.hpp',
'algorithm/update_renderables.cpp',
'api/annotations.cpp',
'api/api_misuse.cpp',
@@ -46,7 +48,6 @@
'geometry/binpack.cpp',
'map/map.cpp',
'map/tile.cpp',
'map/transform.cpp',
'math/minmax.cpp',

View File

@@ -1,118 +1,85 @@
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/test/mock_view.hpp>
#include <gtest/gtest.h>
#include <unordered_set>
using namespace mbgl;
using set = std::unordered_set<TileID>;
TEST(TileCover, Empty) {
auto result = tileCover(LatLngBounds::empty(), 0, 0);
ASSERT_TRUE(result.empty());
EXPECT_EQ((std::vector<UnwrappedTileID>{}), util::tileCover(LatLngBounds::empty(), 0));
}
TEST(TileCover, Arctic) {
auto result = tileCover(LatLngBounds::hull({ 86, -180 }, { 90, 180 }), 0, 0);
ASSERT_TRUE(result.empty());
EXPECT_EQ((std::vector<UnwrappedTileID>{}),
util::tileCover(LatLngBounds::hull({ 86, -180 }, { 90, 180 }), 0));
}
TEST(TileCover, Antarctic) {
auto result = tileCover(LatLngBounds::hull({ -86, -180 }, { -90, 180 }), 0, 0);
ASSERT_TRUE(result.empty());
EXPECT_EQ((std::vector<UnwrappedTileID>{}),
util::tileCover(LatLngBounds::hull({ -86, -180 }, { -90, 180 }), 0));
}
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);
EXPECT_EQ((std::vector<UnwrappedTileID>{
{ 0, 0, 0 },
}),
util::tileCover(LatLngBounds::world(), 0));
}
TEST(TileCover, Pitch) {
MockView view;
Transform transform(view, ConstrainMode::HeightOnly);
transform.resize({ { 512, 512 } });
transform.setZoom(2);
transform.setPitch(40.0 * M_PI / 180.0);
EXPECT_EQ((std::vector<UnwrappedTileID>{
{ 2, 1, 1 }, { 2, 1, 2 }, { 2, 2, 1 }, { 2, 2, 2 },
}),
util::tileCover(transform.getState(), 2));
}
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()
}));
EXPECT_EQ((std::vector<UnwrappedTileID>{
{ 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 },
}),
util::tileCover(LatLngBounds::world(), 1));
}
//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);
//}
TEST(TileCover, SingletonZ0) {
EXPECT_EQ((std::vector<UnwrappedTileID>{}),
util::tileCover(LatLngBounds::singleton({ 0, 0 }), 0));
}
static const LatLngBounds sanFrancisco = LatLngBounds::hull(
{ 37.6609, -122.5744 },
{ 37.8271, -122.3204 });
TEST(TileCover, SingletonZ1) {
EXPECT_EQ((std::vector<UnwrappedTileID>{}),
util::tileCover(LatLngBounds::singleton({ 0, 0 }), 1));
}
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);
EXPECT_EQ((std::vector<UnwrappedTileID>{
{ 0, 0, 0 },
}),
util::tileCover(sanFrancisco, 0));
}
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()
}));
EXPECT_EQ((std::vector<UnwrappedTileID>{
{ 10, 163, 395 }, { 10, 163, 396 }, { 10, 164, 395 }, { 10, 164, 396 },
}),
util::tileCover(sanFrancisco, 10));
}
//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);
//}
static const LatLngBounds sanFranciscoWrapped =
LatLngBounds::hull({ 37.6609, 238.5744 }, { 37.8271, 238.3204 });
TEST(TileCover, SanFranciscoZ0Wrapped) {
EXPECT_EQ((std::vector<UnwrappedTileID>{ { 0, 1, 0 } }),
util::tileCover(sanFranciscoWrapped, 0));
}