[core] offline region definition - add support for arbitrary geometries
This commit is contained in:
committed by
Ivo van Dongen
parent
cf0313f219
commit
fae099933b
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/geometry.hpp>
|
||||
#include <mbgl/util/range.hpp>
|
||||
#include <mbgl/util/optional.hpp>
|
||||
#include <mbgl/util/variant.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
|
||||
@@ -30,22 +32,40 @@ public:
|
||||
OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float);
|
||||
|
||||
/* Private */
|
||||
std::vector<CanonicalTileID> tileCover(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
|
||||
uint64_t tileCount(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
|
||||
const std::string styleURL;
|
||||
const LatLngBounds bounds;
|
||||
const double minZoom;
|
||||
const double maxZoom;
|
||||
const float pixelRatio;
|
||||
private:
|
||||
Range<uint8_t> coveringZoomRange(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
|
||||
};
|
||||
|
||||
/*
|
||||
* For the present, a tile pyramid is the only type of offline region. In the future,
|
||||
* other definition types will be available and this will be a variant type.
|
||||
* An offline region defined by a style URL, geometry, zoom range, and
|
||||
* device pixel ratio.
|
||||
*
|
||||
* Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom.
|
||||
*
|
||||
* maxZoom may be ∞, in which case for each tile source, the region will include
|
||||
* tiles from minZoom up to the maximum zoom level provided by that source.
|
||||
*
|
||||
* pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0.
|
||||
*/
|
||||
using OfflineRegionDefinition = OfflineTilePyramidRegionDefinition;
|
||||
class OfflineGeometryRegionDefinition {
|
||||
public:
|
||||
OfflineGeometryRegionDefinition(std::string styleURL, Geometry<double>, double minZoom, double maxZoom, float pixelRatio);
|
||||
|
||||
/* Private */
|
||||
const std::string styleURL;
|
||||
const Geometry<double> geometry;
|
||||
const double minZoom;
|
||||
const double maxZoom;
|
||||
const float pixelRatio;
|
||||
};
|
||||
|
||||
/*
|
||||
* The offline region definition types supported
|
||||
*/
|
||||
using OfflineRegionDefinition = variant<OfflineTilePyramidRegionDefinition, OfflineGeometryRegionDefinition>;
|
||||
|
||||
/*
|
||||
* The encoded format is private.
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include <mbgl/storage/offline.hpp>
|
||||
#include <mbgl/util/tile_cover.hpp>
|
||||
#include <mbgl/util/tileset.hpp>
|
||||
#include <mbgl/util/projection.hpp>
|
||||
|
||||
#include <mapbox/geojson.hpp>
|
||||
#include <mapbox/geojson/rapidjson.hpp>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
@@ -11,6 +13,8 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
// OfflineTilePyramidRegionDefinition
|
||||
|
||||
OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
|
||||
std::string styleURL_, LatLngBounds bounds_, double minZoom_, double maxZoom_, float pixelRatio_)
|
||||
: styleURL(std::move(styleURL_)),
|
||||
@@ -24,87 +28,100 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
|
||||
const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);
|
||||
|
||||
std::vector<CanonicalTileID> result;
|
||||
// OfflineGeometryRegionDefinition
|
||||
|
||||
for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
|
||||
for (const auto& tile : util::tileCover(bounds, z)) {
|
||||
result.emplace_back(tile.canonical);
|
||||
}
|
||||
OfflineGeometryRegionDefinition::OfflineGeometryRegionDefinition(std::string styleURL_, Geometry<double> geometry_, double minZoom_, double maxZoom_, float pixelRatio_)
|
||||
: styleURL(styleURL_)
|
||||
, geometry(std::move(geometry_))
|
||||
, minZoom(minZoom_)
|
||||
, maxZoom(maxZoom_)
|
||||
, pixelRatio(pixelRatio_) {
|
||||
if (minZoom < 0 || maxZoom < 0 || maxZoom < minZoom || pixelRatio < 0 ||
|
||||
!std::isfinite(minZoom) || std::isnan(maxZoom) || !std::isfinite(pixelRatio)) {
|
||||
throw std::invalid_argument("Invalid offline region definition");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
|
||||
|
||||
const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);
|
||||
unsigned long result = 0;;
|
||||
for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
|
||||
result += util::tileCount(bounds, z);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
|
||||
double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min);
|
||||
double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max);
|
||||
|
||||
assert(minZ >= 0);
|
||||
assert(maxZ >= 0);
|
||||
assert(minZ < std::numeric_limits<uint8_t>::max());
|
||||
assert(maxZ < std::numeric_limits<uint8_t>::max());
|
||||
return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) };
|
||||
}
|
||||
|
||||
OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) {
|
||||
rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
|
||||
doc.Parse<0>(region.c_str());
|
||||
|
||||
if (doc.HasParseError() ||
|
||||
!doc.HasMember("style_url") || !doc["style_url"].IsString() ||
|
||||
!doc.HasMember("bounds") || !doc["bounds"].IsArray() || doc["bounds"].Size() != 4 ||
|
||||
!doc["bounds"][0].IsDouble() || !doc["bounds"][1].IsDouble() ||
|
||||
!doc["bounds"][2].IsDouble() || !doc["bounds"][3].IsDouble() ||
|
||||
!doc.HasMember("min_zoom") || !doc["min_zoom"].IsDouble() ||
|
||||
(doc.HasMember("max_zoom") && !doc["max_zoom"].IsDouble()) ||
|
||||
!doc.HasMember("pixel_ratio") || !doc["pixel_ratio"].IsDouble()) {
|
||||
// validation
|
||||
|
||||
auto hasValidBounds = [&] {
|
||||
return doc.HasMember("bounds") && doc["bounds"].IsArray() && doc["bounds"].Size() == 4
|
||||
&& doc["bounds"][0].IsDouble() && doc["bounds"][1].IsDouble()
|
||||
&& doc["bounds"][2].IsDouble() && doc["bounds"][3].IsDouble();
|
||||
};
|
||||
|
||||
auto hasValidGeometry = [&] {
|
||||
return doc.HasMember("geometry") && doc["geometry"].IsObject();
|
||||
};
|
||||
|
||||
if (doc.HasParseError()
|
||||
|| !doc.HasMember("style_url") || !doc["style_url"].IsString()
|
||||
|| !(hasValidBounds() || hasValidGeometry())
|
||||
|| !doc.HasMember("min_zoom") || !doc["min_zoom"].IsDouble()
|
||||
|| (doc.HasMember("max_zoom") && !doc["max_zoom"].IsDouble())
|
||||
|| !doc.HasMember("pixel_ratio") || !doc["pixel_ratio"].IsDouble()) {
|
||||
throw std::runtime_error("Malformed offline region definition");
|
||||
}
|
||||
|
||||
// Common properties
|
||||
|
||||
std::string styleURL { doc["style_url"].GetString(), doc["style_url"].GetStringLength() };
|
||||
LatLngBounds bounds = LatLngBounds::hull(
|
||||
LatLng(doc["bounds"][0].GetDouble(), doc["bounds"][1].GetDouble()),
|
||||
LatLng(doc["bounds"][2].GetDouble(), doc["bounds"][3].GetDouble()));
|
||||
double minZoom = doc["min_zoom"].GetDouble();
|
||||
double maxZoom = doc.HasMember("max_zoom") ? doc["max_zoom"].GetDouble() : INFINITY;
|
||||
float pixelRatio = doc["pixel_ratio"].GetDouble();
|
||||
|
||||
if (doc.HasMember("bounds")) {
|
||||
return OfflineTilePyramidRegionDefinition{
|
||||
styleURL,
|
||||
LatLngBounds::hull(
|
||||
LatLng(doc["bounds"][0].GetDouble(), doc["bounds"][1].GetDouble()),
|
||||
LatLng(doc["bounds"][2].GetDouble(), doc["bounds"][3].GetDouble())),
|
||||
minZoom, maxZoom, pixelRatio };
|
||||
} else {
|
||||
return OfflineGeometryRegionDefinition{
|
||||
styleURL,
|
||||
mapbox::geojson::convert<Geometry<double>>(doc["geometry"].GetObject()),
|
||||
minZoom, maxZoom, pixelRatio };
|
||||
};
|
||||
|
||||
return { styleURL, bounds, minZoom, maxZoom, pixelRatio };
|
||||
}
|
||||
|
||||
std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region) {
|
||||
rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
|
||||
doc.SetObject();
|
||||
|
||||
doc.AddMember("style_url", rapidjson::StringRef(region.styleURL.data(), region.styleURL.length()), doc.GetAllocator());
|
||||
// Encode common properties
|
||||
region.match([&](auto& _region) {
|
||||
doc.AddMember("style_url", rapidjson::StringRef(_region.styleURL.data(), _region.styleURL.length()), doc.GetAllocator());
|
||||
doc.AddMember("min_zoom", _region.minZoom, doc.GetAllocator());
|
||||
if (std::isfinite(_region.maxZoom)) {
|
||||
doc.AddMember("max_zoom", _region.maxZoom, doc.GetAllocator());
|
||||
}
|
||||
|
||||
rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(rapidjson::kArrayType);
|
||||
bounds.PushBack(region.bounds.south(), doc.GetAllocator());
|
||||
bounds.PushBack(region.bounds.west(), doc.GetAllocator());
|
||||
bounds.PushBack(region.bounds.north(), doc.GetAllocator());
|
||||
bounds.PushBack(region.bounds.east(), doc.GetAllocator());
|
||||
doc.AddMember("bounds", bounds, doc.GetAllocator());
|
||||
doc.AddMember("pixel_ratio", _region.pixelRatio, doc.GetAllocator());
|
||||
});
|
||||
|
||||
doc.AddMember("min_zoom", region.minZoom, doc.GetAllocator());
|
||||
if (std::isfinite(region.maxZoom)) {
|
||||
doc.AddMember("max_zoom", region.maxZoom, doc.GetAllocator());
|
||||
}
|
||||
// Encode specific properties
|
||||
region.match(
|
||||
[&] (const OfflineTilePyramidRegionDefinition& _region) {
|
||||
rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(rapidjson::kArrayType);
|
||||
bounds.PushBack(_region.bounds.south(), doc.GetAllocator());
|
||||
bounds.PushBack(_region.bounds.west(), doc.GetAllocator());
|
||||
bounds.PushBack(_region.bounds.north(), doc.GetAllocator());
|
||||
bounds.PushBack(_region.bounds.east(), doc.GetAllocator());
|
||||
doc.AddMember("bounds", bounds, doc.GetAllocator());
|
||||
|
||||
doc.AddMember("pixel_ratio", region.pixelRatio, doc.GetAllocator());
|
||||
},
|
||||
[&] (const OfflineGeometryRegionDefinition& _region) {
|
||||
doc.AddMember("geometry", mapbox::geojson::convert(_region.geometry, doc.GetAllocator()), doc.GetAllocator());
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
@@ -113,6 +130,9 @@ std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region)
|
||||
return buffer.GetString();
|
||||
}
|
||||
|
||||
|
||||
// OfflineRegion
|
||||
|
||||
OfflineRegion::OfflineRegion(int64_t id_,
|
||||
OfflineRegionDefinition definition_,
|
||||
OfflineRegionMetadata metadata_)
|
||||
@@ -135,5 +155,4 @@ const OfflineRegionMetadata& OfflineRegion::getMetadata() const {
|
||||
int64_t OfflineRegion::getID() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -24,6 +24,63 @@ namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
|
||||
// Generic functions
|
||||
|
||||
template <class RegionDefinition>
|
||||
Range<uint8_t> coveringZoomRange(const RegionDefinition& definition,
|
||||
style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) {
|
||||
double minZ = std::max<double>(util::coveringZoomLevel(definition.minZoom, type, tileSize), zoomRange.min);
|
||||
double maxZ = std::min<double>(util::coveringZoomLevel(definition.maxZoom, type, tileSize), zoomRange.max);
|
||||
|
||||
assert(minZ >= 0);
|
||||
assert(maxZ >= 0);
|
||||
assert(minZ < std::numeric_limits<uint8_t>::max());
|
||||
assert(maxZ < std::numeric_limits<uint8_t>::max());
|
||||
return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) };
|
||||
}
|
||||
|
||||
template <class Geometry, class Fn>
|
||||
void tileCover(const Geometry& geometry, uint8_t z, Fn&& fn) {
|
||||
util::TileCover cover(geometry, z);
|
||||
while (cover.hasNext()) {
|
||||
fn(cover.next()->canonical);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class Fn>
|
||||
void tileCover(const OfflineRegionDefinition& definition, style::SourceType type,
|
||||
uint16_t tileSize, const Range<uint8_t>& zoomRange, Fn&& fn) {
|
||||
const Range<uint8_t> clampedZoomRange =
|
||||
definition.match([&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); });
|
||||
|
||||
for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
|
||||
definition.match(
|
||||
[&](const OfflineTilePyramidRegionDefinition& reg){ tileCover(reg.bounds, z, fn); },
|
||||
[&](const OfflineGeometryRegionDefinition& reg){ tileCover(reg.geometry, z, fn); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t tileCount(const OfflineRegionDefinition& definition, style::SourceType type,
|
||||
uint16_t tileSize, const Range<uint8_t>& zoomRange) {
|
||||
|
||||
const Range<uint8_t> clampedZoomRange =
|
||||
definition.match([&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); });
|
||||
|
||||
unsigned long result = 0;;
|
||||
for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
|
||||
result += definition.match(
|
||||
[&](const OfflineTilePyramidRegionDefinition& reg){ return util::tileCount(reg.bounds, z); },
|
||||
[&](const OfflineGeometryRegionDefinition& reg){ return util::tileCount(reg.geometry, z); }
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// OfflineDownload
|
||||
|
||||
OfflineDownload::OfflineDownload(int64_t id_,
|
||||
OfflineRegionDefinition&& definition_,
|
||||
OfflineDatabase& offlineDatabase_,
|
||||
@@ -70,7 +127,8 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
|
||||
}
|
||||
|
||||
result->requiredResourceCount++;
|
||||
optional<Response> styleResponse = offlineDatabase.get(Resource::style(definition.styleURL));
|
||||
optional<Response> styleResponse =
|
||||
offlineDatabase.get(Resource::style(definition.match([](auto& reg){ return reg.styleURL; })));
|
||||
if (!styleResponse) {
|
||||
return *result;
|
||||
}
|
||||
@@ -86,7 +144,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
|
||||
auto handleTiledSource = [&] (const variant<std::string, Tileset>& urlOrTileset, const uint16_t tileSize) {
|
||||
if (urlOrTileset.is<Tileset>()) {
|
||||
result->requiredResourceCount +=
|
||||
definition.tileCount(type, tileSize, urlOrTileset.get<Tileset>().zoomRange);
|
||||
tileCount(definition, type, tileSize, urlOrTileset.get<Tileset>().zoomRange);
|
||||
} else {
|
||||
result->requiredResourceCount += 1;
|
||||
const auto& url = urlOrTileset.get<std::string>();
|
||||
@@ -96,7 +154,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
|
||||
optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse->data, error);
|
||||
if (tileset) {
|
||||
result->requiredResourceCount +=
|
||||
definition.tileCount(type, tileSize, (*tileset).zoomRange);
|
||||
tileCount(definition, type, tileSize, (*tileset).zoomRange);
|
||||
}
|
||||
} else {
|
||||
result->requiredResourceCountIsPrecise = false;
|
||||
@@ -116,7 +174,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
|
||||
handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case SourceType::RasterDEM: {
|
||||
const auto& rasterDEMSource = *source->as<RasterDEMSource>();
|
||||
handleTiledSource(rasterDEMSource.getURLOrTileset(), rasterDEMSource.getTileSize());
|
||||
@@ -161,7 +219,8 @@ void OfflineDownload::activateDownload() {
|
||||
status = OfflineRegionStatus();
|
||||
status.downloadState = OfflineRegionDownloadState::Active;
|
||||
status.requiredResourceCount++;
|
||||
ensureResource(Resource::style(definition.styleURL), [&](Response styleResponse) {
|
||||
ensureResource(Resource::style(definition.match([](auto& reg){ return reg.styleURL; })),
|
||||
[&](Response styleResponse) {
|
||||
status.requiredResourceCountIsPrecise = true;
|
||||
|
||||
style::Parser parser;
|
||||
@@ -207,7 +266,7 @@ void OfflineDownload::activateDownload() {
|
||||
handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case SourceType::RasterDEM: {
|
||||
const auto& rasterDEMSource = *source->as<RasterDEMSource>();
|
||||
handleTiledSource(rasterDEMSource.getURLOrTileset(), rasterDEMSource.getTileSize());
|
||||
@@ -247,8 +306,9 @@ void OfflineDownload::activateDownload() {
|
||||
}
|
||||
|
||||
if (!parser.spriteURL.empty()) {
|
||||
queueResource(Resource::spriteImage(parser.spriteURL, definition.pixelRatio));
|
||||
queueResource(Resource::spriteJSON(parser.spriteURL, definition.pixelRatio));
|
||||
auto pixelRatio = definition.match([](auto& reg){ return reg.pixelRatio; });
|
||||
queueResource(Resource::spriteImage(parser.spriteURL, pixelRatio));
|
||||
queueResource(Resource::spriteJSON(parser.spriteURL, pixelRatio));
|
||||
}
|
||||
|
||||
continueDownload();
|
||||
@@ -296,11 +356,12 @@ void OfflineDownload::queueResource(Resource resource) {
|
||||
}
|
||||
|
||||
void OfflineDownload::queueTiles(SourceType type, uint16_t tileSize, const Tileset& tileset) {
|
||||
for (const auto& tile : definition.tileCover(type, tileSize, tileset.zoomRange)) {
|
||||
tileCover(definition, type, tileSize, tileset.zoomRange, [&](const auto& tile) {
|
||||
status.requiredResourceCount++;
|
||||
resourcesRemaining.push_back(
|
||||
Resource::tile(tileset.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z, tileset.scheme));
|
||||
}
|
||||
resourcesRemaining.push_back(Resource::tile(
|
||||
tileset.tiles[0], definition.match([](auto& def) { return def.pixelRatio; }), tile.x,
|
||||
tile.y, tile.z, tileset.scheme));
|
||||
});
|
||||
}
|
||||
|
||||
void OfflineDownload::ensureResource(const Resource& resource,
|
||||
|
||||
@@ -6,58 +6,30 @@
|
||||
using namespace mbgl;
|
||||
using SourceType = mbgl::style::SourceType;
|
||||
|
||||
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 });
|
||||
TEST(OfflineTilePyramidRegionDefinition, EncodeDecode) {
|
||||
OfflineTilePyramidRegionDefinition region("mapbox://style", LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }), 0, 20, 1.0);
|
||||
|
||||
TEST(OfflineTilePyramidRegionDefinition, TileCoverEmpty) {
|
||||
OfflineTilePyramidRegionDefinition region("", LatLngBounds::empty(), 0, 20, 1.0);
|
||||
|
||||
EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, { 0, 22 }));
|
||||
auto encoded = encodeOfflineRegionDefinition(region);
|
||||
auto decoded = decodeOfflineRegionDefinition(encoded).get<OfflineTilePyramidRegionDefinition>();
|
||||
|
||||
EXPECT_EQ(decoded.styleURL, region.styleURL);
|
||||
EXPECT_EQ(decoded.minZoom, region.minZoom);
|
||||
EXPECT_EQ(decoded.maxZoom, region.maxZoom);
|
||||
EXPECT_EQ(decoded.pixelRatio, region.pixelRatio);
|
||||
EXPECT_EQ(decoded.bounds.southwest(), region.bounds.southwest());
|
||||
EXPECT_EQ(decoded.bounds.northeast(), region.bounds.northeast());
|
||||
}
|
||||
|
||||
TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomIntersection) {
|
||||
OfflineTilePyramidRegionDefinition region("", sanFrancisco, 2, 2, 1.0);
|
||||
TEST(OfflineGeometryRegionDefinition, EncodeDecode) {
|
||||
OfflineGeometryRegionDefinition region("mapbox://style", Point<double>(-122.5744, 37.6609), 0, 2, 1.0);
|
||||
|
||||
EXPECT_EQ((std::vector<CanonicalTileID>{ { 2, 0, 1 } }),
|
||||
region.tileCover(SourceType::Vector, 512, { 0, 22 }));
|
||||
|
||||
EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, { 3, 22 }));
|
||||
}
|
||||
|
||||
TEST(OfflineTilePyramidRegionDefinition, TileCoverTileSize) {
|
||||
OfflineTilePyramidRegionDefinition region("", LatLngBounds::world(), 0, 0, 1.0);
|
||||
|
||||
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
|
||||
region.tileCover(SourceType::Vector, 512, { 0, 22 }));
|
||||
|
||||
EXPECT_EQ((std::vector<CanonicalTileID>{ { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 } }),
|
||||
region.tileCover(SourceType::Vector, 256, { 0, 22 }));
|
||||
}
|
||||
|
||||
TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomRounding) {
|
||||
OfflineTilePyramidRegionDefinition region("", sanFrancisco, 0.6, 0.7, 1.0);
|
||||
|
||||
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
|
||||
region.tileCover(SourceType::Vector, 512, { 0, 22 }));
|
||||
|
||||
EXPECT_EQ((std::vector<CanonicalTileID>{ { 1, 0, 0 } }),
|
||||
region.tileCover(SourceType::Raster, 512, { 0, 22 }));
|
||||
}
|
||||
|
||||
TEST(OfflineTilePyramidRegionDefinition, TileCoverWrapped) {
|
||||
OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 0, 1.0);
|
||||
|
||||
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
|
||||
region.tileCover(SourceType::Vector, 512, { 0, 22 }));
|
||||
}
|
||||
|
||||
TEST(OfflineTilePyramidRegionDefinition, TileCount) {
|
||||
OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 22, 1.0);
|
||||
|
||||
//These numbers match the count from tileCover().size().
|
||||
EXPECT_EQ(38424u, region.tileCount(SourceType::Vector, 512, { 10, 18 }));
|
||||
EXPECT_EQ(9675240u, region.tileCount(SourceType::Vector, 512, { 3, 22 }));
|
||||
auto encoded = encodeOfflineRegionDefinition(region);
|
||||
auto decoded = decodeOfflineRegionDefinition(encoded).get<OfflineGeometryRegionDefinition>();
|
||||
|
||||
EXPECT_EQ(decoded.styleURL, region.styleURL);
|
||||
EXPECT_EQ(decoded.minZoom, region.minZoom);
|
||||
EXPECT_EQ(decoded.maxZoom, region.maxZoom);
|
||||
EXPECT_EQ(decoded.pixelRatio, region.pixelRatio);
|
||||
EXPECT_EQ(decoded.geometry, region.geometry);
|
||||
}
|
||||
|
||||
@@ -411,25 +411,31 @@ TEST(OfflineDatabase, PutTileNotFound) {
|
||||
TEST(OfflineDatabase, CreateRegion) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:");
|
||||
OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineRegionMetadata metadata {{ 1, 2, 3 }};
|
||||
auto region = db.createRegion(definition, metadata);
|
||||
ASSERT_TRUE(region);
|
||||
|
||||
EXPECT_EQ(definition.styleURL, region->getDefinition().styleURL);
|
||||
EXPECT_EQ(definition.bounds, region->getDefinition().bounds);
|
||||
EXPECT_EQ(definition.minZoom, region->getDefinition().minZoom);
|
||||
EXPECT_EQ(definition.maxZoom, region->getDefinition().maxZoom);
|
||||
EXPECT_EQ(definition.pixelRatio, region->getDefinition().pixelRatio);
|
||||
EXPECT_EQ(metadata, region->getMetadata());
|
||||
|
||||
EXPECT_EQ(0u, log.uncheckedCount());
|
||||
|
||||
region->getDefinition().match(
|
||||
[&](OfflineTilePyramidRegionDefinition& def) {
|
||||
EXPECT_EQ(definition.styleURL, def.styleURL);
|
||||
EXPECT_EQ(definition.bounds, def.bounds);
|
||||
EXPECT_EQ(definition.minZoom, def.minZoom);
|
||||
EXPECT_EQ(definition.maxZoom, def.maxZoom);
|
||||
EXPECT_EQ(definition.pixelRatio, def.pixelRatio);
|
||||
}, [](auto&) {
|
||||
EXPECT_FALSE(false);
|
||||
}
|
||||
);
|
||||
EXPECT_EQ(metadata, region->getMetadata());
|
||||
}
|
||||
|
||||
TEST(OfflineDatabase, UpdateMetadata) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:");
|
||||
OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineRegionMetadata metadata {{ 1, 2, 3 }};
|
||||
auto region = db.createRegion(definition, metadata);
|
||||
ASSERT_TRUE(region);
|
||||
@@ -445,7 +451,7 @@ TEST(OfflineDatabase, UpdateMetadata) {
|
||||
TEST(OfflineDatabase, ListRegions) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:");
|
||||
OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineRegionMetadata metadata {{ 1, 2, 3 }};
|
||||
|
||||
auto region = db.createRegion(definition, metadata);
|
||||
@@ -453,12 +459,19 @@ TEST(OfflineDatabase, ListRegions) {
|
||||
auto regions = db.listRegions().value();
|
||||
|
||||
ASSERT_EQ(1u, regions.size());
|
||||
|
||||
EXPECT_EQ(region->getID(), regions.at(0).getID());
|
||||
EXPECT_EQ(definition.styleURL, regions.at(0).getDefinition().styleURL);
|
||||
EXPECT_EQ(definition.bounds, regions.at(0).getDefinition().bounds);
|
||||
EXPECT_EQ(definition.minZoom, regions.at(0).getDefinition().minZoom);
|
||||
EXPECT_EQ(definition.maxZoom, regions.at(0).getDefinition().maxZoom);
|
||||
EXPECT_EQ(definition.pixelRatio, regions.at(0).getDefinition().pixelRatio);
|
||||
regions.at(0).getDefinition().match(
|
||||
[&](OfflineTilePyramidRegionDefinition& def) {
|
||||
EXPECT_EQ(definition.styleURL, def.styleURL);
|
||||
EXPECT_EQ(definition.bounds, def.bounds);
|
||||
EXPECT_EQ(definition.minZoom, def.minZoom);
|
||||
EXPECT_EQ(definition.maxZoom, def.maxZoom);
|
||||
EXPECT_EQ(definition.pixelRatio, def.pixelRatio);
|
||||
},
|
||||
[&](auto&) {
|
||||
EXPECT_FALSE(false);
|
||||
});
|
||||
EXPECT_EQ(metadata, regions.at(0).getMetadata());
|
||||
|
||||
EXPECT_EQ(0u, log.uncheckedCount());
|
||||
@@ -467,27 +480,30 @@ TEST(OfflineDatabase, ListRegions) {
|
||||
TEST(OfflineDatabase, GetRegionDefinition) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:");
|
||||
OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineRegionMetadata metadata {{ 1, 2, 3 }};
|
||||
|
||||
auto region = db.createRegion(definition, metadata);
|
||||
ASSERT_TRUE(region);
|
||||
auto result = db.getRegionDefinition(region->getID());
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
EXPECT_EQ(definition.styleURL, result->styleURL);
|
||||
EXPECT_EQ(definition.bounds, result->bounds);
|
||||
EXPECT_EQ(definition.minZoom, result->minZoom);
|
||||
EXPECT_EQ(definition.maxZoom, result->maxZoom);
|
||||
EXPECT_EQ(definition.pixelRatio, result->pixelRatio);
|
||||
|
||||
EXPECT_EQ(0u, log.uncheckedCount());
|
||||
|
||||
auto region = db.createRegion(definition, metadata);
|
||||
db.getRegionDefinition(region->getID())->match(
|
||||
[&](OfflineTilePyramidRegionDefinition& result) {
|
||||
EXPECT_EQ(definition.styleURL, result.styleURL);
|
||||
EXPECT_EQ(definition.bounds, result.bounds);
|
||||
EXPECT_EQ(definition.minZoom, result.minZoom);
|
||||
EXPECT_EQ(definition.maxZoom, result.maxZoom);
|
||||
EXPECT_EQ(definition.pixelRatio, result.pixelRatio);
|
||||
},
|
||||
[&](auto&) {
|
||||
EXPECT_FALSE(false);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
TEST(OfflineDatabase, DeleteRegion) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:");
|
||||
OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineRegionMetadata metadata {{ 1, 2, 3 }};
|
||||
auto region = db.createRegion(definition, metadata);
|
||||
ASSERT_TRUE(region);
|
||||
@@ -509,15 +525,17 @@ TEST(OfflineDatabase, DeleteRegion) {
|
||||
TEST(OfflineDatabase, CreateRegionInfiniteMaxZoom) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:");
|
||||
OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
OfflineRegionMetadata metadata;
|
||||
auto region = db.createRegion(definition, metadata);
|
||||
ASSERT_TRUE(region);
|
||||
|
||||
EXPECT_EQ(0, region->getDefinition().minZoom);
|
||||
EXPECT_EQ(INFINITY, region->getDefinition().maxZoom);
|
||||
|
||||
EXPECT_EQ(0u, log.uncheckedCount());
|
||||
|
||||
region->getDefinition().match([&](auto& def) {
|
||||
EXPECT_EQ(0, def.minZoom);
|
||||
EXPECT_EQ(INFINITY, def.maxZoom);
|
||||
});
|
||||
}
|
||||
|
||||
TEST(OfflineDatabase, TEST_REQUIRES_WRITE(ConcurrentUse)) {
|
||||
@@ -600,7 +618,7 @@ TEST(OfflineDatabase, PutEvictsLeastRecentlyUsedResources) {
|
||||
TEST(OfflineDatabase, PutRegionResourceDoesNotEvict) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:", 1024 * 100);
|
||||
OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
auto region = db.createRegion(definition, OfflineRegionMetadata());
|
||||
ASSERT_TRUE(region);
|
||||
|
||||
@@ -637,7 +655,7 @@ TEST(OfflineDatabase, PutFailsWhenEvictionInsuffices) {
|
||||
TEST(OfflineDatabase, GetRegionCompletedStatus) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:");
|
||||
OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineRegionMetadata metadata;
|
||||
auto region = db.createRegion(definition, metadata);
|
||||
ASSERT_TRUE(region);
|
||||
@@ -676,7 +694,7 @@ TEST(OfflineDatabase, GetRegionCompletedStatus) {
|
||||
TEST(OfflineDatabase, HasRegionResource) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:", 1024 * 100);
|
||||
OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
auto region = db.createRegion(definition, OfflineRegionMetadata());
|
||||
ASSERT_TRUE(region);
|
||||
|
||||
@@ -700,7 +718,7 @@ TEST(OfflineDatabase, HasRegionResource) {
|
||||
TEST(OfflineDatabase, HasRegionResourceTile) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:", 1024 * 100);
|
||||
OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
auto region = db.createRegion(definition, OfflineRegionMetadata());
|
||||
ASSERT_TRUE(region);
|
||||
|
||||
@@ -734,7 +752,7 @@ TEST(OfflineDatabase, HasRegionResourceTile) {
|
||||
TEST(OfflineDatabase, OfflineMapboxTileCount) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:");
|
||||
OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
|
||||
OfflineRegionMetadata metadata;
|
||||
|
||||
auto region1 = db.createRegion(definition, metadata);
|
||||
@@ -795,7 +813,7 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) {
|
||||
TEST(OfflineDatabase, BatchInsertion) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:", 1024 * 100);
|
||||
OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
auto region = db.createRegion(definition, OfflineRegionMetadata());
|
||||
ASSERT_TRUE(region);
|
||||
|
||||
@@ -821,7 +839,7 @@ TEST(OfflineDatabase, BatchInsertionMapboxTileCountExceeded) {
|
||||
FixtureLog log;
|
||||
OfflineDatabase db(":memory:", 1024 * 100);
|
||||
db.setOfflineMapboxTileCountLimit(1);
|
||||
OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
|
||||
auto region = db.createRegion(definition, OfflineRegionMetadata());
|
||||
ASSERT_TRUE(region);
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
std::size_t size = 0;
|
||||
|
||||
auto createRegion() {
|
||||
OfflineRegionDefinition definition { "", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 1.0 };
|
||||
OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 1.0 };
|
||||
OfflineRegionMetadata metadata;
|
||||
return db.createRegion(definition, metadata);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user