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

View File

@@ -16,4 +16,13 @@ namespace mbgl {
template <typename... 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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
#include <mbgl/storage/offline.hpp>
#include <mbgl/util/tileset.hpp>
#include <mbgl/util/projection.hpp>
#include <mbgl/util/variant.hpp>
#include <mapbox/geojson.hpp>
#include <mapbox/geojson/rapidjson.hpp>
@@ -110,32 +111,37 @@ std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region)
doc.SetObject();
// 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());
}
std::visit(
[&](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());
}
doc.AddMember("pixel_ratio", _region.pixelRatio, doc.GetAllocator());
doc.AddMember("include_ideographs", _region.includeIdeographs, doc.GetAllocator());
});
doc.AddMember("pixel_ratio", _region.pixelRatio, doc.GetAllocator());
doc.AddMember("include_ideographs", _region.includeIdeographs, doc.GetAllocator());
},
region);
// 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());
},
[&](const OfflineGeometryRegionDefinition& _region) {
doc.AddMember(
"geometry", mapbox::geojson::convert(_region.geometry, doc.GetAllocator()), doc.GetAllocator());
});
std::visit(overloaded{[&](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());
},
[&](const OfflineGeometryRegionDefinition& _region) {
doc.AddMember("geometry",
mapbox::geojson::convert(_region.geometry, doc.GetAllocator()),
doc.GetAllocator());
}},
region);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

View File

@@ -63,12 +63,15 @@ void tileCover(const OfflineRegionDefinition& definition,
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); });
const Range<uint8_t> clampedZoomRange = std::visit(
[&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }, definition);
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); });
std::visit(overloaded{[&](const OfflineTilePyramidRegionDefinition& reg) { tileCover(reg.bounds, z, fn); },
[&](const OfflineGeometryRegionDefinition& reg) {
tileCover(reg.geometry, z, fn);
}},
definition);
}
}
@@ -76,14 +79,17 @@ 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); });
const Range<uint8_t> clampedZoomRange = std::visit(
[&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }, definition);
uint64_t result{};
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); });
result += std::visit(
overloaded{[&](const OfflineTilePyramidRegionDefinition& reg) { return util::tileCount(reg.bounds, z); },
[&](const OfflineGeometryRegionDefinition& reg) {
return util::tileCount(reg.geometry, z);
}},
definition);
}
return result;
@@ -138,7 +144,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
result->requiredResourceCount++;
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) {
return *result;
}
@@ -219,7 +225,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
if (!parser.glyphURL.empty()) {
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
: NON_IDEOGRAPH_GLYPH_RANGES_PER_FONT_STACK);
}
@@ -236,7 +242,7 @@ void OfflineDownload::activateDownload() {
status.downloadState = OfflineRegionDownloadState::Active;
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.setUsage(Resource::Usage::Offline);
@@ -330,7 +336,7 @@ void OfflineDownload::activateDownload() {
}
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 (char16_t i = 0; i < GLYPH_RANGES_PER_FONT_STACK; i++) {
// 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++;
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.y,
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);
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.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);
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.minZoom, region.minZoom);

View File

@@ -9,8 +9,10 @@
#include <mbgl/util/string.hpp>
#include <mbgl/storage/sqlite3.hpp>
#include <mbgl/util/variant.hpp>
#include <thread>
#include <random>
#include <variant>
using namespace std::literals::string_literals;
using namespace mbgl;
@@ -437,16 +439,18 @@ TEST(OfflineDatabase, CreateRegion) {
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);
EXPECT_EQ(definition.includeIdeographs, def.includeIdeographs);
},
[](auto&) { EXPECT_FALSE(false); });
std::visit(overloaded{[&](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);
EXPECT_EQ(definition.includeIdeographs, def.includeIdeographs);
},
[](auto&) {
EXPECT_FALSE(false);
}},
region->getDefinition());
EXPECT_EQ(metadata, region->getMetadata());
}
@@ -481,16 +485,18 @@ TEST(OfflineDatabase, ListRegions) {
ASSERT_EQ(1u, regions.size());
EXPECT_EQ(region->getID(), regions.at(0).getID());
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);
EXPECT_EQ(definition.includeIdeographs, def.includeIdeographs);
},
[&](auto&) { EXPECT_FALSE(false); });
std::visit(overloaded{[&](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);
EXPECT_EQ(definition.includeIdeographs, def.includeIdeographs);
},
[&](auto&) {
EXPECT_FALSE(false);
}},
regions.at(0).getDefinition());
EXPECT_EQ(metadata, regions.at(0).getMetadata());
EXPECT_EQ(0u, log.uncheckedCount());
@@ -506,17 +512,20 @@ TEST(OfflineDatabase, GetRegionDefinition) {
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);
EXPECT_EQ(definition.includeIdeographs, result.includeIdeographs);
},
[&](auto&) { EXPECT_FALSE(false); });
auto regionDefinition = db.getRegionDefinition(region->getID()).value();
std::visit(overloaded{[&](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);
EXPECT_EQ(definition.includeIdeographs, result.includeIdeographs);
},
[&](auto&) {
EXPECT_FALSE(false);
}},
regionDefinition);
}
// 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());
region->getDefinition().match([&](auto& def) {
EXPECT_EQ(0, def.minZoom);
EXPECT_EQ(INFINITY, def.maxZoom);
});
std::visit(
[&](auto& def) {
EXPECT_EQ(0, def.minZoom);
EXPECT_EQ(INFINITY, def.maxZoom);
},
region->getDefinition());
}
#ifndef __QT__ // Qt doesn't support concurrent access to the same database.