Use std::variant with OfflineRegionDefinition (#2482)

This commit is contained in:
Bart Louwers
2024-06-11 16:45:09 +02:00
committed by GitHub
parent d82f722a68
commit 8c743d2467
8 changed files with 127 additions and 92 deletions

View File

@@ -3,13 +3,13 @@
#include <mbgl/util/geo.hpp> #include <mbgl/util/geo.hpp>
#include <mbgl/util/geometry.hpp> #include <mbgl/util/geometry.hpp>
#include <mbgl/util/range.hpp> #include <mbgl/util/range.hpp>
#include <mbgl/util/variant.hpp>
#include <mbgl/style/types.hpp> #include <mbgl/style/types.hpp>
#include <mbgl/storage/response.hpp> #include <mbgl/storage/response.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <variant>
namespace mbgl { namespace mbgl {
@@ -71,7 +71,7 @@ public:
/* /*
* The offline region definition types supported * The offline region definition types supported
*/ */
using OfflineRegionDefinition = variant<OfflineTilePyramidRegionDefinition, OfflineGeometryRegionDefinition>; using OfflineRegionDefinition = std::variant<OfflineTilePyramidRegionDefinition, OfflineGeometryRegionDefinition>;
/* /*
* The encoded format is private. * The encoded format is private.

View File

@@ -16,4 +16,13 @@ namespace mbgl {
template <typename... T> template <typename... T>
using variant = mapbox::util::variant<T...>; using variant = mapbox::util::variant<T...>;
// https://en.cppreference.com/w/cpp/utility/variant/visit
template <class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
// explicit deduction guide (not needed as of C++20)
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
} // namespace mbgl } // namespace mbgl

View File

@@ -1,8 +1,11 @@
#include "offline_region.hpp" #include "offline_region.hpp"
#include <variant>
#include <mbgl/storage/file_source_manager.hpp> #include <mbgl/storage/file_source_manager.hpp>
#include <mbgl/util/logging.hpp> #include <mbgl/util/logging.hpp>
#include <mbgl/util/string.hpp> #include <mbgl/util/string.hpp>
#include <mbgl/util/variant.hpp>
#include "offline_region_definition.hpp" #include "offline_region_definition.hpp"
#include "offline_region_error.hpp" #include "offline_region_error.hpp"
@@ -180,13 +183,13 @@ jni::Local<jni::Object<OfflineRegion>> OfflineRegion::New(jni::JNIEnv& env,
const jni::Object<FileSource>& jFileSource, const jni::Object<FileSource>& jFileSource,
mbgl::OfflineRegion region) { mbgl::OfflineRegion region) {
// Definition // Definition
auto definition = region.getDefinition().match( auto definition = std::visit(overloaded{[&](const mbgl::OfflineTilePyramidRegionDefinition def) {
[&](const mbgl::OfflineTilePyramidRegionDefinition def) { return OfflineTilePyramidRegionDefinition::New(env, def);
return OfflineTilePyramidRegionDefinition::New(env, def); },
}, [&](const mbgl::OfflineGeometryRegionDefinition def) {
[&](const mbgl::OfflineGeometryRegionDefinition def) { return OfflineGeometryRegionDefinition::New(env, def);
return OfflineGeometryRegionDefinition::New(env, def); }},
}); region.getDefinition());
// Create region java object // Create region java object
static auto& javaClass = jni::Class<OfflineRegion>::Singleton(env); static auto& javaClass = jni::Class<OfflineRegion>::Singleton(env);

View File

@@ -12,6 +12,7 @@
#include <mbgl/map/map_options.hpp> #include <mbgl/map/map_options.hpp>
#include <mbgl/storage/database_file_source.hpp> #include <mbgl/storage/database_file_source.hpp>
#include <mbgl/util/variant.hpp>
const MLNExceptionName MLNInvalidOfflinePackException = @"MLNInvalidOfflinePackException"; const MLNExceptionName MLNInvalidOfflinePackException = @"MLNInvalidOfflinePackException";
@@ -93,15 +94,14 @@ private:
MLNAssert([MLNTilePyramidOfflineRegion conformsToProtocol:@protocol(MLNOfflineRegion_Private)], @"MLNTilePyramidOfflineRegion should conform to MLNOfflineRegion_Private."); MLNAssert([MLNTilePyramidOfflineRegion conformsToProtocol:@protocol(MLNOfflineRegion_Private)], @"MLNTilePyramidOfflineRegion should conform to MLNOfflineRegion_Private.");
MLNAssert([MLNShapeOfflineRegion conformsToProtocol:@protocol(MLNOfflineRegion_Private)], @"MLNShapeOfflineRegion should conform to MLNOfflineRegion_Private."); MLNAssert([MLNShapeOfflineRegion conformsToProtocol:@protocol(MLNOfflineRegion_Private)], @"MLNShapeOfflineRegion should conform to MLNOfflineRegion_Private.");
return std::visit(mbgl::overloaded{
[&] (const mbgl::OfflineTilePyramidRegionDefinition def){
return regionDefinition.match( return (id <MLNOfflineRegion>)[[MLNTilePyramidOfflineRegion alloc] initWithOfflineRegionDefinition:def];
[&] (const mbgl::OfflineTilePyramidRegionDefinition def){ },
return (id <MLNOfflineRegion>)[[MLNTilePyramidOfflineRegion alloc] initWithOfflineRegionDefinition:def]; [&] (const mbgl::OfflineGeometryRegionDefinition& def){
}, return (id <MLNOfflineRegion>)[[MLNShapeOfflineRegion alloc] initWithOfflineRegionDefinition:def];
[&] (const mbgl::OfflineGeometryRegionDefinition& def){ }
return (id <MLNOfflineRegion>)[[MLNShapeOfflineRegion alloc] initWithOfflineRegionDefinition:def]; }, regionDefinition);
});
} }
- (NSData *)context { - (NSData *)context {

View File

@@ -1,6 +1,7 @@
#include <mbgl/storage/offline.hpp> #include <mbgl/storage/offline.hpp>
#include <mbgl/util/tileset.hpp> #include <mbgl/util/tileset.hpp>
#include <mbgl/util/projection.hpp> #include <mbgl/util/projection.hpp>
#include <mbgl/util/variant.hpp>
#include <mapbox/geojson.hpp> #include <mapbox/geojson.hpp>
#include <mapbox/geojson/rapidjson.hpp> #include <mapbox/geojson/rapidjson.hpp>
@@ -110,32 +111,37 @@ std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region)
doc.SetObject(); doc.SetObject();
// Encode common properties // Encode common properties
region.match([&](auto& _region) { std::visit(
doc.AddMember( [&](auto& _region) {
"style_url", rapidjson::StringRef(_region.styleURL.data(), _region.styleURL.length()), doc.GetAllocator()); doc.AddMember("style_url",
doc.AddMember("min_zoom", _region.minZoom, doc.GetAllocator()); rapidjson::StringRef(_region.styleURL.data(), _region.styleURL.length()),
if (std::isfinite(_region.maxZoom)) { doc.GetAllocator());
doc.AddMember("max_zoom", _region.maxZoom, doc.GetAllocator()); doc.AddMember("min_zoom", _region.minZoom, doc.GetAllocator());
} if (std::isfinite(_region.maxZoom)) {
doc.AddMember("max_zoom", _region.maxZoom, doc.GetAllocator());
}
doc.AddMember("pixel_ratio", _region.pixelRatio, doc.GetAllocator()); doc.AddMember("pixel_ratio", _region.pixelRatio, doc.GetAllocator());
doc.AddMember("include_ideographs", _region.includeIdeographs, doc.GetAllocator()); doc.AddMember("include_ideographs", _region.includeIdeographs, doc.GetAllocator());
}); },
region);
// Encode specific properties // Encode specific properties
region.match( std::visit(overloaded{[&](const OfflineTilePyramidRegionDefinition& _region) {
[&](const OfflineTilePyramidRegionDefinition& _region) { rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(
rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(rapidjson::kArrayType); rapidjson::kArrayType);
bounds.PushBack(_region.bounds.south(), doc.GetAllocator()); bounds.PushBack(_region.bounds.south(), doc.GetAllocator());
bounds.PushBack(_region.bounds.west(), doc.GetAllocator()); bounds.PushBack(_region.bounds.west(), doc.GetAllocator());
bounds.PushBack(_region.bounds.north(), doc.GetAllocator()); bounds.PushBack(_region.bounds.north(), doc.GetAllocator());
bounds.PushBack(_region.bounds.east(), doc.GetAllocator()); bounds.PushBack(_region.bounds.east(), doc.GetAllocator());
doc.AddMember("bounds", bounds, doc.GetAllocator()); doc.AddMember("bounds", bounds, doc.GetAllocator());
}, },
[&](const OfflineGeometryRegionDefinition& _region) { [&](const OfflineGeometryRegionDefinition& _region) {
doc.AddMember( doc.AddMember("geometry",
"geometry", mapbox::geojson::convert(_region.geometry, doc.GetAllocator()), doc.GetAllocator()); mapbox::geojson::convert(_region.geometry, doc.GetAllocator()),
}); doc.GetAllocator());
}},
region);
rapidjson::StringBuffer buffer; rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

View File

@@ -63,12 +63,15 @@ void tileCover(const OfflineRegionDefinition& definition,
uint16_t tileSize, uint16_t tileSize,
const Range<uint8_t>& zoomRange, const Range<uint8_t>& zoomRange,
Fn&& fn) { Fn&& fn) {
const Range<uint8_t> clampedZoomRange = definition.match( const Range<uint8_t> clampedZoomRange = std::visit(
[&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }); [&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }, definition);
for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
definition.match([&](const OfflineTilePyramidRegionDefinition& reg) { tileCover(reg.bounds, z, fn); }, std::visit(overloaded{[&](const OfflineTilePyramidRegionDefinition& reg) { tileCover(reg.bounds, z, fn); },
[&](const OfflineGeometryRegionDefinition& reg) { tileCover(reg.geometry, z, fn); }); [&](const OfflineGeometryRegionDefinition& reg) {
tileCover(reg.geometry, z, fn);
}},
definition);
} }
} }
@@ -76,14 +79,17 @@ uint64_t tileCount(const OfflineRegionDefinition& definition,
style::SourceType type, style::SourceType type,
uint16_t tileSize, uint16_t tileSize,
const Range<uint8_t>& zoomRange) { const Range<uint8_t>& zoomRange) {
const Range<uint8_t> clampedZoomRange = definition.match( const Range<uint8_t> clampedZoomRange = std::visit(
[&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }); [&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }, definition);
uint64_t result{}; uint64_t result{};
for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
result += definition.match( result += std::visit(
[&](const OfflineTilePyramidRegionDefinition& reg) { return util::tileCount(reg.bounds, z); }, overloaded{[&](const OfflineTilePyramidRegionDefinition& reg) { return util::tileCount(reg.bounds, z); },
[&](const OfflineGeometryRegionDefinition& reg) { return util::tileCount(reg.geometry, z); }); [&](const OfflineGeometryRegionDefinition& reg) {
return util::tileCount(reg.geometry, z);
}},
definition);
} }
return result; return result;
@@ -138,7 +144,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
result->requiredResourceCount++; result->requiredResourceCount++;
std::optional<Response> styleResponse = offlineDatabase.get( std::optional<Response> styleResponse = offlineDatabase.get(
Resource::style(definition.match([](auto& reg) { return reg.styleURL; }))); Resource::style(std::visit([](auto& reg) { return reg.styleURL; }, definition)));
if (!styleResponse) { if (!styleResponse) {
return *result; return *result;
} }
@@ -219,7 +225,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
if (!parser.glyphURL.empty()) { if (!parser.glyphURL.empty()) {
result->requiredResourceCount += parser.fontStacks().size() * result->requiredResourceCount += parser.fontStacks().size() *
(definition.match([](auto& reg) { return reg.includeIdeographs; }) (std::visit([](auto& reg) { return reg.includeIdeographs; }, definition)
? GLYPH_RANGES_PER_FONT_STACK ? GLYPH_RANGES_PER_FONT_STACK
: NON_IDEOGRAPH_GLYPH_RANGES_PER_FONT_STACK); : NON_IDEOGRAPH_GLYPH_RANGES_PER_FONT_STACK);
} }
@@ -236,7 +242,7 @@ void OfflineDownload::activateDownload() {
status.downloadState = OfflineRegionDownloadState::Active; status.downloadState = OfflineRegionDownloadState::Active;
status.requiredResourceCount++; status.requiredResourceCount++;
auto styleResource = Resource::style(definition.match([](auto& reg) { return reg.styleURL; })); auto styleResource = Resource::style(std::visit([](auto& reg) { return reg.styleURL; }, definition));
styleResource.setPriority(Resource::Priority::Low); styleResource.setPriority(Resource::Priority::Low);
styleResource.setUsage(Resource::Usage::Offline); styleResource.setUsage(Resource::Usage::Offline);
@@ -330,7 +336,7 @@ void OfflineDownload::activateDownload() {
} }
if (!parser.glyphURL.empty()) { if (!parser.glyphURL.empty()) {
const bool includeIdeographs = definition.match([](auto& reg) { return reg.includeIdeographs; }); const bool includeIdeographs = std::visit([](auto& reg) { return reg.includeIdeographs; }, definition);
for (const auto& fontStack : parser.fontStacks()) { for (const auto& fontStack : parser.fontStacks()) {
for (char16_t i = 0; i < GLYPH_RANGES_PER_FONT_STACK; i++) { for (char16_t i = 0; i < GLYPH_RANGES_PER_FONT_STACK; i++) {
// Assumes that if a glyph range starts with fixed width/ideographic // Assumes that if a glyph range starts with fixed width/ideographic
@@ -438,7 +444,7 @@ void OfflineDownload::queueTiles(SourceType type, uint16_t tileSize, const Tiles
status.requiredTileCount++; status.requiredTileCount++;
auto tileResource = Resource::tile(tileset.tiles[0], auto tileResource = Resource::tile(tileset.tiles[0],
definition.match([](auto& def) { return def.pixelRatio; }), std::visit([](auto& def) { return def.pixelRatio; }, definition),
tile.x, tile.x,
tile.y, tile.y,
tile.z, tile.z,

View File

@@ -11,7 +11,7 @@ TEST(OfflineTilePyramidRegionDefinition, EncodeDecode) {
"maptiler://style", LatLngBounds::hull({37.6609, -122.5744}, {37.8271, -122.3204}), 0, 20, 1.0, true); "maptiler://style", LatLngBounds::hull({37.6609, -122.5744}, {37.8271, -122.3204}), 0, 20, 1.0, true);
auto encoded = encodeOfflineRegionDefinition(region); auto encoded = encodeOfflineRegionDefinition(region);
auto decoded = decodeOfflineRegionDefinition(encoded).get<OfflineTilePyramidRegionDefinition>(); auto decoded = std::get<OfflineTilePyramidRegionDefinition>(decodeOfflineRegionDefinition(encoded));
EXPECT_EQ(decoded.styleURL, region.styleURL); EXPECT_EQ(decoded.styleURL, region.styleURL);
EXPECT_EQ(decoded.minZoom, region.minZoom); EXPECT_EQ(decoded.minZoom, region.minZoom);
@@ -26,7 +26,7 @@ TEST(OfflineGeometryRegionDefinition, EncodeDecode) {
OfflineGeometryRegionDefinition region("maptiler://style", Point<double>(-122.5744, 37.6609), 0, 2, 1.0, false); OfflineGeometryRegionDefinition region("maptiler://style", Point<double>(-122.5744, 37.6609), 0, 2, 1.0, false);
auto encoded = encodeOfflineRegionDefinition(region); auto encoded = encodeOfflineRegionDefinition(region);
auto decoded = decodeOfflineRegionDefinition(encoded).get<OfflineGeometryRegionDefinition>(); auto decoded = std::get<OfflineGeometryRegionDefinition>(decodeOfflineRegionDefinition(encoded));
EXPECT_EQ(decoded.styleURL, region.styleURL); EXPECT_EQ(decoded.styleURL, region.styleURL);
EXPECT_EQ(decoded.minZoom, region.minZoom); EXPECT_EQ(decoded.minZoom, region.minZoom);

View File

@@ -9,8 +9,10 @@
#include <mbgl/util/string.hpp> #include <mbgl/util/string.hpp>
#include <mbgl/storage/sqlite3.hpp> #include <mbgl/storage/sqlite3.hpp>
#include <mbgl/util/variant.hpp>
#include <thread> #include <thread>
#include <random> #include <random>
#include <variant>
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
using namespace mbgl; using namespace mbgl;
@@ -437,16 +439,18 @@ TEST(OfflineDatabase, CreateRegion) {
EXPECT_EQ(0u, log.uncheckedCount()); EXPECT_EQ(0u, log.uncheckedCount());
region->getDefinition().match( std::visit(overloaded{[&](OfflineTilePyramidRegionDefinition& def) {
[&](OfflineTilePyramidRegionDefinition& def) { EXPECT_EQ(definition.styleURL, def.styleURL);
EXPECT_EQ(definition.styleURL, def.styleURL); EXPECT_EQ(definition.bounds, def.bounds);
EXPECT_EQ(definition.bounds, def.bounds); EXPECT_EQ(definition.minZoom, def.minZoom);
EXPECT_EQ(definition.minZoom, def.minZoom); EXPECT_EQ(definition.maxZoom, def.maxZoom);
EXPECT_EQ(definition.maxZoom, def.maxZoom); EXPECT_EQ(definition.pixelRatio, def.pixelRatio);
EXPECT_EQ(definition.pixelRatio, def.pixelRatio); EXPECT_EQ(definition.includeIdeographs, def.includeIdeographs);
EXPECT_EQ(definition.includeIdeographs, def.includeIdeographs); },
}, [](auto&) {
[](auto&) { EXPECT_FALSE(false); }); EXPECT_FALSE(false);
}},
region->getDefinition());
EXPECT_EQ(metadata, region->getMetadata()); EXPECT_EQ(metadata, region->getMetadata());
} }
@@ -481,16 +485,18 @@ TEST(OfflineDatabase, ListRegions) {
ASSERT_EQ(1u, regions.size()); ASSERT_EQ(1u, regions.size());
EXPECT_EQ(region->getID(), regions.at(0).getID()); EXPECT_EQ(region->getID(), regions.at(0).getID());
regions.at(0).getDefinition().match( std::visit(overloaded{[&](OfflineTilePyramidRegionDefinition& def) {
[&](OfflineTilePyramidRegionDefinition& def) { EXPECT_EQ(definition.styleURL, def.styleURL);
EXPECT_EQ(definition.styleURL, def.styleURL); EXPECT_EQ(definition.bounds, def.bounds);
EXPECT_EQ(definition.bounds, def.bounds); EXPECT_EQ(definition.minZoom, def.minZoom);
EXPECT_EQ(definition.minZoom, def.minZoom); EXPECT_EQ(definition.maxZoom, def.maxZoom);
EXPECT_EQ(definition.maxZoom, def.maxZoom); EXPECT_EQ(definition.pixelRatio, def.pixelRatio);
EXPECT_EQ(definition.pixelRatio, def.pixelRatio); EXPECT_EQ(definition.includeIdeographs, def.includeIdeographs);
EXPECT_EQ(definition.includeIdeographs, def.includeIdeographs); },
}, [&](auto&) {
[&](auto&) { EXPECT_FALSE(false); }); EXPECT_FALSE(false);
}},
regions.at(0).getDefinition());
EXPECT_EQ(metadata, regions.at(0).getMetadata()); EXPECT_EQ(metadata, regions.at(0).getMetadata());
EXPECT_EQ(0u, log.uncheckedCount()); EXPECT_EQ(0u, log.uncheckedCount());
@@ -506,17 +512,20 @@ TEST(OfflineDatabase, GetRegionDefinition) {
EXPECT_EQ(0u, log.uncheckedCount()); EXPECT_EQ(0u, log.uncheckedCount());
auto region = db.createRegion(definition, metadata); auto region = db.createRegion(definition, metadata);
db.getRegionDefinition(region->getID()) auto regionDefinition = db.getRegionDefinition(region->getID()).value();
->match(
[&](OfflineTilePyramidRegionDefinition& result) { std::visit(overloaded{[&](OfflineTilePyramidRegionDefinition& result) {
EXPECT_EQ(definition.styleURL, result.styleURL); EXPECT_EQ(definition.styleURL, result.styleURL);
EXPECT_EQ(definition.bounds, result.bounds); EXPECT_EQ(definition.bounds, result.bounds);
EXPECT_EQ(definition.minZoom, result.minZoom); EXPECT_EQ(definition.minZoom, result.minZoom);
EXPECT_EQ(definition.maxZoom, result.maxZoom); EXPECT_EQ(definition.maxZoom, result.maxZoom);
EXPECT_EQ(definition.pixelRatio, result.pixelRatio); EXPECT_EQ(definition.pixelRatio, result.pixelRatio);
EXPECT_EQ(definition.includeIdeographs, result.includeIdeographs); EXPECT_EQ(definition.includeIdeographs, result.includeIdeographs);
}, },
[&](auto&) { EXPECT_FALSE(false); }); [&](auto&) {
EXPECT_FALSE(false);
}},
regionDefinition);
} }
// Disabled due to flakiness: https://github.com/mapbox/mapbox-gl-native/issues/14966 // Disabled due to flakiness: https://github.com/mapbox/mapbox-gl-native/issues/14966
@@ -1016,10 +1025,12 @@ TEST(OfflineDatabase, CreateRegionInfiniteMaxZoom) {
EXPECT_EQ(0u, log.uncheckedCount()); EXPECT_EQ(0u, log.uncheckedCount());
region->getDefinition().match([&](auto& def) { std::visit(
EXPECT_EQ(0, def.minZoom); [&](auto& def) {
EXPECT_EQ(INFINITY, def.maxZoom); EXPECT_EQ(0, def.minZoom);
}); EXPECT_EQ(INFINITY, def.maxZoom);
},
region->getDefinition());
} }
#ifndef __QT__ // Qt doesn't support concurrent access to the same database. #ifndef __QT__ // Qt doesn't support concurrent access to the same database.